Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalContracts v0.5.0
Vealic.LethalContracts.dll
Decompiled 3 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalContracts.Contracts; using LethalContracts.UI; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Vealic.LethalContracts")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("LethalContracts")] [assembly: AssemblyTitle("Vealic.LethalContracts")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace LethalContracts { [BepInPlugin("Vealic.LethalContracts", "LethalContracts", "1.0.0")] public class LethalContracts : BaseUnityPlugin { public static LethalContracts Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static Harmony? Harmony { get; set; } private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; Instance = this; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); Type[] array = types; foreach (Type type in array) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo in array2) { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0) { methodInfo.Invoke(null, null); } } } Assets.Load(); Patch(); Logger.LogInfo((object)"Vealic.LethalContracts v1.0.0 has loaded!"); } internal static void Patch() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (Harmony == null) { Harmony = new Harmony("Vealic.LethalContracts"); } Logger.LogDebug((object)"Patching..."); Harmony.PatchAll(); Logger.LogDebug((object)"Finished patching!"); } internal static void Unpatch() { Logger.LogDebug((object)"Unpatching..."); Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } Logger.LogDebug((object)"Finished unpatching!"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Vealic.LethalContracts"; public const string PLUGIN_NAME = "LethalContracts"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace LethalContracts.UI { internal static class Assets { public static Sprite? BarFull; public static Sprite? BarEmpty; public static Sprite? FulfilledScreen; public static Sprite? MoneyBill; public static AudioClip? Beep; public static void Load() { BarEmpty = LoadSprite("LethalContracts.Assets.hud_contract_bar.png"); BarFull = LoadSprite("LethalContracts.Assets.hud_contract_bar_full.png"); FulfilledScreen = LoadSprite("LethalContracts.Assets.contract_fulfilled_screen.png"); MoneyBill = LoadSprite("LethalContracts.Assets.hud_money_1.png"); Beep = MakeBeep(); } private static Sprite? LoadSprite(string resourceName) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); if (stream == null) { LethalContracts.Logger.LogError((object)("Missing resource: " + resourceName)); return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { filterMode = (FilterMode)0 }; ImageConversion.LoadImage(val, memoryStream.ToArray()); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } private static AudioClip MakeBeep() { AudioClip val = AudioClip.Create("contract_beep", 6615, 1, 44100, false); float[] array = new float[6615]; for (int i = 0; i < 6615; i++) { float num = (float)i / 44100f; float num2 = Mathf.Min(1f, (float)(6615 - i) / 1500f); array[i] = Mathf.Sin(MathF.PI * 2400f * num) * 0.5f * num2; } val.SetData(array, 0); return val; } } public class ContractFulfilledScreen : MonoBehaviour { private readonly Queue<int> queue = new Queue<int>(); private bool playing; private CanvasGroup group = null; private RectTransform panelRect = null; private Transform billParent = null; public static ContractFulfilledScreen? Instance { get; private set; } public static void Create(Transform parent) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("ContractFulfilledScreen"); val.transform.SetParent(parent, false); Instance = val.AddComponent<ContractFulfilledScreen>(); Instance.Build(); } } private void Build() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) RectTransform val = ((Component)this).gameObject.AddComponent<RectTransform>(); val.anchorMin = Vector2.zero; val.anchorMax = Vector2.one; val.offsetMin = Vector2.zero; val.offsetMax = Vector2.zero; group = ((Component)this).gameObject.AddComponent<CanvasGroup>(); group.alpha = 0f; group.blocksRaycasts = false; group.interactable = false; GameObject val2 = new GameObject("Bills"); val2.transform.SetParent(((Component)this).transform, false); RectTransform val3 = val2.AddComponent<RectTransform>(); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = Vector2.zero; val3.offsetMax = Vector2.zero; billParent = val2.transform; GameObject val4 = new GameObject("Panel"); val4.transform.SetParent(((Component)this).transform, false); panelRect = val4.AddComponent<RectTransform>(); RectTransform obj = panelRect; RectTransform obj2 = panelRect; Vector2 val5 = default(Vector2); ((Vector2)(ref val5))..ctor(0.5f, 0.5f); obj2.anchorMax = val5; obj.anchorMin = val5; panelRect.pivot = new Vector2(0.5f, 0.5f); panelRect.sizeDelta = new Vector2(420f, 190f); Image val6 = val4.AddComponent<Image>(); val6.sprite = Assets.FulfilledScreen; val6.preserveAspect = true; } public void Enqueue(int reward) { queue.Enqueue(reward); if (!playing) { ((MonoBehaviour)this).StartCoroutine(PlayQueue()); } } private IEnumerator PlayQueue() { playing = true; yield return (object)new WaitForSeconds(8f); while (queue.Count > 0) { yield return Play(queue.Dequeue()); yield return (object)new WaitForSeconds(0.4f); } playing = false; } private IEnumerator Play(int reward) { for (int i = 0; i < 14; i++) { SpawnBill(); } float t = 0f; while (t < 0.25f) { t += Time.deltaTime; float k = t / 0.25f; group.alpha = k; ((Transform)panelRect).localScale = Vector3.one * Mathf.Lerp(0.85f, 1f, k); yield return null; } group.alpha = 1f; ((Transform)panelRect).localScale = Vector3.one; yield return (object)new WaitForSeconds(4f); t = 0f; while (t < 0.4f) { t += Time.deltaTime; group.alpha = 1f - t / 0.4f; yield return null; } group.alpha = 0f; } private void SpawnBill() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Bill"); val.transform.SetParent(billParent, false); RectTransform val2 = val.AddComponent<RectTransform>(); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 1f); val2.anchorMax = val3; val2.anchorMin = val3; val2.pivot = new Vector2(0.5f, 0.5f); val2.sizeDelta = new Vector2(70f, 30f); Image val4 = val.AddComponent<Image>(); val4.sprite = Assets.MoneyBill; val4.preserveAspect = true; ((MonoBehaviour)this).StartCoroutine(FallBill(val2, val)); } private IEnumerator FallBill(RectTransform rt, GameObject go) { float x = Random.Range(-800f, 800f); float delay = Random.Range(0f, 3f); float speed = Random.Range(260f, 480f); float spin = Random.Range(-90f, 90f); float sway = Random.Range(20f, 60f); float swaySpeed = Random.Range(1.5f, 3.5f); rt.anchoredPosition = new Vector2(x, 120f); yield return (object)new WaitForSeconds(delay); float y = 120f; float elapsed = 0f; while (y > -1200f) { elapsed += Time.deltaTime; y -= speed * Time.deltaTime; rt.anchoredPosition = new Vector2(x + Mathf.Sin(elapsed * swaySpeed) * sway, y); ((Transform)rt).localRotation = Quaternion.Euler(0f, 0f, elapsed * spin); yield return null; } Object.Destroy((Object)(object)go); } } public class ContractHUD : MonoBehaviour { private int index; private TextMeshProUGUI title = null; private TextMeshProUGUI counter = null; private Image fill = null; private CanvasGroup group = null; public static ContractHUD? Instance { get; private set; } public static void Create(Transform parent) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("ContractHUD"); val.transform.SetParent(parent, false); Instance = val.AddComponent<ContractHUD>(); Instance.Build(); } } private void Build() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) group = ((Component)this).gameObject.AddComponent<CanvasGroup>(); RectTransform val = ((Component)this).gameObject.AddComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(1f, 1f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(1f, 1f); val.anchoredPosition = new Vector2(-40f, -60f); val.sizeDelta = new Vector2(260f, 70f); title = MakeText("Title", new Vector2(0f, -2f), 14f, (TextAlignmentOptions)257); counter = MakeText("Counter", new Vector2(0f, -2f), 14f, (TextAlignmentOptions)260); Image val3 = MakeImage("BarEmpty", Assets.BarEmpty); fill = MakeImage("BarFill", Assets.BarFull); fill.type = (Type)3; fill.fillMethod = (FillMethod)0; fill.fillOrigin = 0; fill.fillAmount = 0f; Refresh(); } private TextMeshProUGUI MakeText(string name, Vector2 pos, float size, TextAlignmentOptions align) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(((Component)this).transform, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0.5f, 1f); val2.sizeDelta = new Vector2(0f, 20f); val2.anchoredPosition = pos; TextMeshProUGUI val3 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val3).fontSize = size; ((TMP_Text)val3).alignment = align; ((Graphic)val3).color = new Color(0.4f, 1f, 0.4f, 1f); HUDManager instance = HUDManager.Instance; object font; if (instance == null) { font = null; } else { TextMeshProUGUI obj = instance.controlTipLines[0]; font = ((obj != null) ? ((TMP_Text)obj).font : null); } ((TMP_Text)val3).font = (TMP_FontAsset)font; return val3; } private Image MakeImage(string name, Sprite? sprite) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(((Component)this).transform, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(0f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(0.5f, 0f); val2.sizeDelta = new Vector2(0f, 18f); val2.anchoredPosition = new Vector2(0f, 30f); Image val3 = val.AddComponent<Image>(); val3.sprite = sprite; return val3; } private void Update() { List<Contract> accepted = ContractState.Accepted; if (accepted.Count == 0) { group.alpha = 0f; return; } group.alpha = 1f; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val == (Object)null) && !val.inTerminalMenu && !val.isTypingChat && accepted.Count > 1 && Keyboard.current != null) { if (((ButtonControl)Keyboard.current.leftArrowKey).wasPressedThisFrame) { index = (index - 1 + accepted.Count) % accepted.Count; } else if (((ButtonControl)Keyboard.current.rightArrowKey).wasPressedThisFrame) { index = (index + 1) % accepted.Count; } } Refresh(); } public void Refresh() { List<Contract> accepted = ContractState.Accepted; if (accepted.Count != 0) { index = Mathf.Clamp(index, 0, accepted.Count - 1); Contract contract = accepted[index]; string text = ((accepted.Count > 1) ? $" ‹ {index + 1}/{accepted.Count} ›" : ""); ((TMP_Text)title).text = contract.Title + text; ((TMP_Text)counter).text = $"{contract.Progress}/{contract.Goal}"; fill.fillAmount = contract.BarFill; } } } } namespace LethalContracts.Patches { [HarmonyPatch] public class ContractSpawnPatch { private static bool spawnedThisRound; [HarmonyPatch(typeof(RoundManager), "GeneratedFloorPostProcessing")] [HarmonyPostfix] private static void SpawnContractEnemies() { LethalContracts.Logger.LogInfo((object)($"GeneratedFloorPostProcessing fired. server={NetworkManager.Singleton.IsServer}, " + $"guard={spawnedThisRound}, accepted={ContractState.Accepted.Count}")); if (!NetworkManager.Singleton.IsServer || spawnedThisRound) { return; } spawnedThisRound = true; RoundManager instance = RoundManager.Instance; ManualLogSource logger = LethalContracts.Logger; GameObject[] insideAINodes = instance.insideAINodes; string text = $"Contract spawn pass: inside={((insideAINodes != null) ? insideAINodes.Length : (-1))}, "; GameObject[] outsideAINodes = instance.outsideAINodes; logger.LogInfo((object)(text + $"outside={((outsideAINodes != null) ? outsideAINodes.Length : (-1))}, power={instance.currentEnemyPower}")); foreach (Contract item in ContractState.Accepted) { if (1 == 0) { } string text2 = ((item is HuntContract huntContract) ? huntContract.EnemyName : ((!(item is ObservationContract observationContract)) ? "" : observationContract.EnemyName)); if (1 == 0) { } string text3 = text2; if (string.IsNullOrEmpty(text3)) { continue; } EnemyType val = FindEnemyType(text3); if ((Object)(object)val == (Object)null) { LethalContracts.Logger.LogWarning((object)("No EnemyType found for '" + text3 + "'")); continue; } int num = ((item.Goal >= 2) ? 1 : 0); for (int i = 0; i < item.Goal + num; i++) { SpawnOne(instance, val); } } foreach (RetrievalContract item2 in ContractState.Accepted.OfType<RetrievalContract>()) { Item val2 = FindItem(item2.ItemName); if ((Object)(object)val2 == (Object)null) { LethalContracts.Logger.LogWarning((object)("No Item found for '" + item2.ItemName + "'")); continue; } GrabbableObject val3 = SpawnScrap(instance, val2, item2); if (!((Object)(object)val3 == (Object)null)) { item2.TargetNetId = ((NetworkBehaviour)val3).NetworkObjectId; ContractNetworkHandler.Instance?.MarkTargetClientRpc(item2.Id, item2.TargetNetId, val3.scrapValue); LethalContracts.Logger.LogInfo((object)$"Retrieval target: {item2.ItemName} (net {item2.TargetNetId}, value {val3.scrapValue})"); } } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] private static void ResetGuard() { spawnedThisRound = false; } private static void SpawnOne(RoundManager rm, EnemyType type) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) GameObject[] array = (type.isOutsideEnemy ? rm.outsideAINodes : rm.insideAINodes); if (array == null || array.Length == 0) { LethalContracts.Logger.LogWarning((object)$"No AI nodes available for {type.enemyName} (outside={type.isOutsideEnemy})"); return; } Vector3 position = array[rm.AnomalyRandom.Next(array.Length)].transform.position; Vector3 randomNavMeshPositionInBoxPredictable = rm.GetRandomNavMeshPositionInBoxPredictable(position, 10f, rm.navHit, rm.AnomalyRandom, -1, 1f); rm.SpawnEnemyGameObject(randomNavMeshPositionInBoxPredictable, 0f, -1, type); float num = type.PowerLevel * 0.5f; if (type.isOutsideEnemy) { rm.currentOutsideEnemyPower += num; } else { rm.currentEnemyPower += num; } LethalContracts.Logger.LogInfo((object)$"Spawned {type.enemyName} (power {type.PowerLevel}) at {randomNavMeshPositionInBoxPredictable} for contract"); } private static EnemyType? FindEnemyType(string enemyName) { SelectableLevel currentLevel = RoundManager.Instance.currentLevel; List<SpawnableEnemyWithRarity>[] array = new List<SpawnableEnemyWithRarity>[3] { currentLevel.Enemies, currentLevel.OutsideEnemies, currentLevel.DaytimeEnemies }; foreach (List<SpawnableEnemyWithRarity> list in array) { foreach (SpawnableEnemyWithRarity item in list) { if ((Object)(object)item.enemyType != (Object)null && item.enemyType.enemyName == enemyName) { return item.enemyType; } } } SelectableLevel[] levels = StartOfRound.Instance.levels; foreach (SelectableLevel val in levels) { List<SpawnableEnemyWithRarity>[] array2 = new List<SpawnableEnemyWithRarity>[3] { val.Enemies, val.OutsideEnemies, val.DaytimeEnemies }; foreach (List<SpawnableEnemyWithRarity> list2 in array2) { foreach (SpawnableEnemyWithRarity item2 in list2) { if ((Object)(object)item2.enemyType != (Object)null && item2.enemyType.enemyName == enemyName) { return item2.enemyType; } } } } return null; } private static Item? FindItem(string itemName) { SelectableLevel currentLevel = RoundManager.Instance.currentLevel; foreach (SpawnableItemWithRarity item in currentLevel.spawnableScrap) { if ((Object)(object)item.spawnableItem != (Object)null && item.spawnableItem.itemName == itemName) { return item.spawnableItem; } } SelectableLevel[] levels = StartOfRound.Instance.levels; foreach (SelectableLevel val in levels) { foreach (SpawnableItemWithRarity item2 in val.spawnableScrap) { if ((Object)(object)item2.spawnableItem != (Object)null && item2.spawnableItem.itemName == itemName) { return item2.spawnableItem; } } } return null; } private static GrabbableObject? SpawnScrap(RoundManager rm, Item item, RetrievalContract c) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) RandomScrapSpawn[] array = (from s in Object.FindObjectsOfType<RandomScrapSpawn>() where !s.spawnUsed select s).ToArray(); if (array.Length == 0) { return null; } Vector3 entrance = RoundManager.FindMainEntrancePosition(true, false); RandomScrapSpawn[] array2 = array.OrderBy((RandomScrapSpawn s) => Vector3.Distance(((Component)s).transform.position, entrance)).ToArray(); int num; int num2; switch (c.Difficulty) { case Difficulty.Easy: num = 0; num2 = array2.Length / 3; break; case Difficulty.Medium: num = array2.Length / 3; num2 = array2.Length * 2 / 3; break; default: num = array2.Length * 2 / 3; num2 = array2.Length; break; } if (num2 <= num) { num = 0; num2 = array2.Length; } RandomScrapSpawn val = array2[rm.AnomalyRandom.Next(num, num2)]; val.spawnUsed = true; Vector3 val2 = rm.GetRandomNavMeshPositionInBoxPredictable(((Component)val).transform.position, val.itemSpawnRange, rm.navHit, rm.AnomalyRandom, -1, 1f) + Vector3.up * item.verticalOffset; GameObject val3 = Object.Instantiate<GameObject>(item.spawnPrefab, val2, Quaternion.identity, rm.spawnedScrapContainer); GrabbableObject component = val3.GetComponent<GrabbableObject>(); ((Component)component).transform.rotation = Quaternion.Euler(item.restingRotation); component.fallTime = 0f; component.scrapValue = c.Reward; val3.GetComponent<NetworkObject>().Spawn(false); return component; } } [HarmonyPatch(typeof(HUDManager))] public class HUDPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void AttachHUD(HUDManager __instance) { ContractHUD.Create(__instance.HUDContainer.transform); ContractFulfilledScreen.Create(__instance.HUDContainer.transform); } } [HarmonyPatch(typeof(EnemyAI))] public class HuntPatch { [HarmonyPatch("KillEnemy")] [HarmonyPostfix] private static void OnKill(EnemyAI __instance) { if (!NetworkManager.Singleton.IsServer || (Object)(object)__instance == (Object)null || (Object)(object)__instance.enemyType == (Object)null) { return; } string enemyName = __instance.enemyType.enemyName; foreach (HuntContract item in ContractState.Accepted.OfType<HuntContract>()) { if (!(item.EnemyName != enemyName) && !item.IsComplete) { item.Progress++; ContractNetworkHandler.Instance?.PushProgress(item.Id, item.Progress); LethalContracts.Logger.LogInfo((object)$"Kill counted: {enemyName} {item.Progress}/{item.Goal}"); } } } } [HarmonyPatch] public class LifeInsurancePatch { public static void Evaluate() { if (!NetworkManager.Singleton.IsServer) { return; } LifeInsuranceContract[] array = ContractState.Accepted.OfType<LifeInsuranceContract>().ToArray(); if (array.Length == 0) { return; } StartOfRound instance = StartOfRound.Instance; bool flag = false; for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if (val.isPlayerDead) { flag = true; LethalContracts.Logger.LogInfo((object)$"Player #{i} is dead — LifeInsurance failed."); break; } } LethalContracts.Logger.LogInfo((object)$"LifeInsurance evaluate: anyDead={flag}"); if (!flag) { return; } LifeInsuranceContract[] array2 = array; foreach (LifeInsuranceContract lifeInsuranceContract in array2) { if (lifeInsuranceContract.Progress != 0) { lifeInsuranceContract.Progress = 0; ContractNetworkHandler.Instance?.PushProgress(lifeInsuranceContract.Id, 0); } } } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] [HarmonyPostfix] private static void OnKill() { Evaluate(); } [HarmonyPatch(typeof(StartOfRound), "ReviveDeadPlayers")] [HarmonyPrefix] private static void BeforeRevive() { Evaluate(); if (NetworkManager.Singleton.IsServer) { ContractState.DespawnRetrievalItems(); } } } [HarmonyPatch(typeof(HUDManager))] public class ObservationPatch { private static readonly HashSet<ulong> reported = new HashSet<ulong>(); [HarmonyPatch("AssignNodeToUIElement")] [HarmonyPostfix] private static void OnScan(ScanNodeProperties node, Dictionary<RectTransform, ScanNodeProperties> ___scanNodes) { if (!((Object)(object)node == (Object)null) && ___scanNodes.ContainsValue(node)) { EnemyAI componentInParent = ((Component)node).GetComponentInParent<EnemyAI>(); if (!((Object)(object)componentInParent == (Object)null) && !componentInParent.isEnemyDead && reported.Add(((NetworkBehaviour)componentInParent).NetworkObjectId)) { ContractNetworkHandler.Instance?.ReportScanServerRpc(((NetworkBehaviour)componentInParent).NetworkObjectId, componentInParent.enemyType.enemyName); } } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] private static void ClearReported() { reported.Clear(); } } [HarmonyPatch(typeof(Terminal))] public class TerminalPatch { [HarmonyPatch("ParsePlayerSentence")] [HarmonyPostfix] private static void ParsePlayerSentence(Terminal __instance, ref TerminalNode __result) { string text = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded); if ("contracts" == text.ToString().Trim().ToLower()) { __result = MakeNode(BuildOfferText()); } else if (text.StartsWith("accept")) { string s = text.Substring(6).Trim(); if (!int.TryParse(s, out var result) || result < 1 || result > ContractState.Offered.Count) { __result = MakeNode("INVALID CONTRACT NUMBER.\n\n"); return; } Contract contract = ContractState.Offered[result - 1]; if (!StartOfRound.Instance.inShipPhase) { __result = MakeNode("CONTRACTS MAY ONLY BE SIGNED BEFORE DEPARTURE.\n\n"); return; } if (ContractState.IsAccepted(contract.Id)) { __result = MakeNode("CONTRACT ALREADY SIGNED.\n\n"); return; } ContractNetworkHandler.Instance?.AcceptContractServerRpc(contract.Id); __result = MakeNode($"SIGNED: {contract.Title}\nREWARD {contract.Reward} PENALTY {contract.Penalty}\n\n"); } else { if (!(text == "killall")) { return; } if (!NetworkManager.Singleton.IsServer) { __result = MakeNode("HOST ONLY.\n\n"); return; } EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>(); int num = 0; EnemyAI[] array2 = array; foreach (EnemyAI val in array2) { if (!((Object)(object)val == (Object)null) && !val.isEnemyDead) { val.KillEnemyOnOwnerClient(true); num++; } } __result = MakeNode($"KILLED {num} ENEMIES.\n\n"); } } private static TerminalNode MakeNode(string text) { TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>(); val.clearPreviousText = true; val.displayText = text; return val; } private static string BuildOfferText() { StringBuilder stringBuilder = new StringBuilder("ACTIVE CONTRACT OFFERS\n\n"); for (int i = 0; i < ContractState.Offered.Count; i++) { Contract contract = ContractState.Offered[i]; string text = (ContractState.IsAccepted(contract.Id) ? "[SIGNED]" : ""); stringBuilder.AppendLine($"[{i + 1}] {contract.Difficulty.ToString().ToUpper()} - {contract.Title} {text}"); stringBuilder.AppendLine(" " + contract.Description); stringBuilder.AppendLine($" REWARD {contract.Reward} PENALTY {contract.Penalty}"); stringBuilder.AppendLine(); } stringBuilder.AppendLine("Type ACCEPT [n] to sign.\n"); return stringBuilder.ToString(); } } } namespace LethalContracts.Contracts { public enum Difficulty { Easy, Medium, Hard } public abstract class Contract { public int Id; public Difficulty Difficulty; public string Title = ""; public string Description = ""; public int Reward; public int Penalty; private int progress; public int Goal = 1; public int PoolIndex = -1; public int Progress { get { return progress; } set { progress = Mathf.Clamp(value, 0, Goal); } } public bool IsComplete => Progress >= Goal; public virtual float BarFill => (Goal == 0) ? 0f : Mathf.Clamp01((float)Progress / (float)Goal); } public class HuntContract : Contract { public string EnemyName = ""; } public class ObservationContract : Contract { public string EnemyName = ""; public readonly HashSet<ulong> Scanned = new HashSet<ulong>(); } public class LifeInsuranceContract : Contract { } public class RetrievalContract : Contract { public string ItemName = ""; public ulong TargetNetId; public bool Finalized; } public enum ContractType { Hunt, Observation, LifeInsurance, Retrieval } public record ContractTemplate(ContractType Type, string TargetName, string DisplayName, int Reward, int Penalty, Difficulty Difficulty, int MinCount, int MaxCount); [HarmonyPatch] public static class ContractGen { internal static readonly ContractTemplate[] Pool = new ContractTemplate[15] { new ContractTemplate(ContractType.Hunt, "Hoarding bug", "Hoarding Bug", 60, 25, Difficulty.Easy, 1, 2), new ContractTemplate(ContractType.Hunt, "Centipede", "Snare Flea", 55, 20, Difficulty.Easy, 1, 2), new ContractTemplate(ContractType.Hunt, "Crawler", "Half", 130, 50, Difficulty.Medium, 1, 2), new ContractTemplate(ContractType.Hunt, "Masked", "Mimic", 150, 60, Difficulty.Medium, 1, 1), new ContractTemplate(ContractType.Hunt, "Flowerman", "Bracken", 220, 100, Difficulty.Hard, 1, 1), new ContractTemplate(ContractType.Hunt, "Nutcracker", "Nutcracker", 240, 110, Difficulty.Hard, 1, 1), new ContractTemplate(ContractType.Observation, "Hoarding bug", "Hoarding Bug", 45, 15, Difficulty.Easy, 2, 3), new ContractTemplate(ContractType.Observation, "Blob", "Hygrodere", 50, 20, Difficulty.Easy, 1, 2), new ContractTemplate(ContractType.Observation, "Crawler", "Half", 110, 40, Difficulty.Medium, 2, 3), new ContractTemplate(ContractType.Observation, "Flowerman", "Bracken", 180, 70, Difficulty.Hard, 1, 2), new ContractTemplate(ContractType.LifeInsurance, "", "", 50, 80, Difficulty.Medium, 1, 1), new ContractTemplate(ContractType.Retrieval, "Magic 7 ball", "Magic 7 Ball", 90, 35, Difficulty.Easy, 1, 1), new ContractTemplate(ContractType.Retrieval, "Rubber Ducky", "Rubber Ducky", 90, 35, Difficulty.Easy, 1, 1), new ContractTemplate(ContractType.Retrieval, "Toy robot", "Robot toy", 165, 65, Difficulty.Medium, 1, 1), new ContractTemplate(ContractType.Retrieval, "Gold bar", "Gold Bar", 250, 95, Difficulty.Hard, 1, 1) }; [HarmonyPatch(typeof(StartOfRound), "SetPlanetsWeather")] [HarmonyPostfix] private static void GenerateForDay() { if (NetworkManager.Singleton.IsServer) { ContractState.Redeem(); ContractState.ClearForNewDay(); Random rng = new Random(); AddOne(rng, Difficulty.Easy); AddOne(rng, Difficulty.Medium); AddOne(rng, Difficulty.Hard); ContractNetworkHandler.Instance?.BroadcastContracts(); LethalContracts.Logger.LogInfo((object)$"Generated {ContractState.Offered.Count} contracts."); } } private static void AddOne(Random rng, Difficulty diff) { (ContractTemplate, int)[] array = (from x in Pool.Select((ContractTemplate t, int i) => (Template: t, Index: i)) where x.Template.Difficulty == diff && IsEnabled(x.Template) select x).ToArray(); if (array.Length != 0) { (ContractTemplate, int) tuple = array[rng.Next(array.Length)]; int count = rng.Next(tuple.Item1.MinCount, tuple.Item1.MaxCount + 1); ContractState.Offered.Add(Build(tuple.Item1, ContractState.Offered.Count, count, tuple.Item2)); } } private static bool IsEnabled(ContractTemplate t) { return true; } public static Contract Build(ContractTemplate t, int id, int count, int poolIndex) { ContractType type = t.Type; if (1 == 0) { } Contract result = type switch { ContractType.Hunt => new HuntContract { Id = id, PoolIndex = poolIndex, Difficulty = t.Difficulty, Title = "HUNT", Description = ((count == 1) ? ("Hunt down and kill a " + t.DisplayName + ".") : $"Hunt down and kill {count} {t.DisplayName}s."), Reward = t.Reward, Penalty = t.Penalty, Goal = count, EnemyName = t.TargetName }, ContractType.Observation => new ObservationContract { Id = id, PoolIndex = poolIndex, Difficulty = t.Difficulty, Title = "OBSERVATION", Description = $"Scan {count} distinct {t.DisplayName}s.", Reward = t.Reward, Penalty = t.Penalty, Goal = count, EnemyName = t.TargetName }, ContractType.Retrieval => new RetrievalContract { Id = id, PoolIndex = poolIndex, Difficulty = t.Difficulty, Title = "RETRIEVAL", Description = "Recover the flagged " + t.DisplayName + " and secure it aboard the ship.", Reward = t.Reward, Penalty = t.Penalty, Goal = 1, ItemName = t.TargetName }, _ => new LifeInsuranceContract { Id = id, PoolIndex = poolIndex, Difficulty = t.Difficulty, Title = "LIFE INSURANCE", Description = "Return with no crewmate deaths.", Reward = t.Reward, Penalty = t.Penalty, Goal = 1, Progress = 1 }, }; if (1 == 0) { } return result; } } public class ContractMarker : MonoBehaviour { private Light? glow; private AudioSource? source; private float nextBeep; private void Start() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ContractGlow"); val.transform.SetParent(((Component)this).transform, false); glow = val.AddComponent<Light>(); glow.color = new Color(1f, 0.15f, 0.15f); glow.range = 7f; glow.intensity = 2f; source = ((Component)this).gameObject.AddComponent<AudioSource>(); source.clip = Assets.Beep; source.spatialBlend = 1f; source.minDistance = 1f; source.maxDistance = 18f; source.volume = 0.35f; } private void Update() { if ((Object)(object)glow != (Object)null) { glow.intensity = 1f + Mathf.PingPong(Time.time * 1.6f, 2.5f); } if ((Object)(object)source != (Object)null && Time.time >= nextBeep) { nextBeep = Time.time + 1.4f; source.Play(); } } public void Stop() { ((Behaviour)this).enabled = false; if ((Object)(object)source != (Object)null) { source.Stop(); } if ((Object)(object)glow != (Object)null) { ((Behaviour)glow).enabled = false; } } } public static class ContractState { public static readonly List<Contract> Offered = new List<Contract>(); public static readonly List<Contract> Accepted = new List<Contract>(); public static bool IsAccepted(int id) { return Accepted.Any((Contract c) => c.Id == id); } public static void Accept(int id) { Contract contract = Offered.FirstOrDefault((Contract x) => x.Id == id); if (contract != null && !IsAccepted(id)) { Accepted.Add(contract); } } public static (int[] PoolIndices, int[] Goals) ToSync() { int[] array = new int[Offered.Count]; int[] array2 = new int[Offered.Count]; for (int i = 0; i < Offered.Count; i++) { array[i] = Offered[i].PoolIndex; array2[i] = Offered[i].Goal; } return (PoolIndices: array, Goals: array2); } public static void RebuildFromSync(int[] poolIndices, int[] goals) { Offered.Clear(); Accepted.Clear(); for (int i = 0; i < poolIndices.Length; i++) { int num = poolIndices[i]; if (num >= 0 && num < ContractGen.Pool.Length) { Offered.Add(ContractGen.Build(ContractGen.Pool[num], i, goals[i], num)); } } LethalContracts.Logger.LogInfo((object)$"Rebuilt {Offered.Count} contracts from host."); } public static void RebuildAccepted(int[] acceptedIds) { Accepted.Clear(); foreach (int id in acceptedIds) { Contract contract = Offered.FirstOrDefault((Contract x) => x.Id == id); if (contract != null) { Accepted.Add(contract); } } } public static void Redeem() { if (Accepted.Count == 0) { return; } int num = 0; int num2 = 0; foreach (Contract item in Accepted) { if (item.IsComplete) { num += item.Reward; num2 += item.Reward; ContractNetworkHandler.Instance?.ContractFulfilledClientRpc(item.Reward); } else { num -= item.Penalty; } LethalContracts.Logger.LogInfo((object)string.Format("{0} {1}/{2} -> {3}", item.Title, item.Progress, item.Goal, item.IsComplete ? "PAID" : "FAILED")); } Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val != (Object)null) { val.groupCredits = Mathf.Max(0, val.groupCredits + num); } if (num2 > 0 && (Object)(object)TimeOfDay.Instance != (Object)null) { TimeOfDay instance = TimeOfDay.Instance; instance.quotaFulfilled += num2; LethalContracts.Logger.LogInfo((object)$"Contract earnings toward quota: +{num2}"); } } public static void DespawnRetrievalItems() { foreach (RetrievalContract item in Accepted.OfType<RetrievalContract>()) { if (item.TargetNetId != 0 && NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(item.TargetNetId, out var value)) { LethalContracts.Logger.LogInfo((object)$"Despawning retrieval item (net {item.TargetNetId}, progress {item.Progress}/{item.Goal})"); item.Finalized = true; value.Despawn(true); } } } public static void ClearForNewDay() { Offered.Clear(); Accepted.Clear(); } } public class ContractNetworkHandler : NetworkBehaviour { private float nextCheck; public static ContractNetworkHandler? Instance { get; private set; } public override void OnNetworkSpawn() { Instance = this; ((NetworkBehaviour)this).OnNetworkSpawn(); if (!((NetworkBehaviour)this).IsServer) { RequestStateServerRpc(NetworkManager.Singleton.LocalClientId); } } public void BroadcastContracts() { if (((NetworkBehaviour)this).IsServer) { var (poolIndices, goals) = ContractState.ToSync(); SyncContractsClientRpc(poolIndices, goals); } } [ClientRpc] private void SyncContractsClientRpc(int[] poolIndices, int[] goals) { if (!((NetworkBehaviour)this).IsServer) { ContractState.RebuildFromSync(poolIndices, goals); } } [ServerRpc(RequireOwnership = false)] public void AcceptContractServerRpc(int contractId) { if (StartOfRound.Instance.inShipPhase && !ContractState.IsAccepted(contractId)) { ContractState.Accept(contractId); SyncAcceptedClientRpc(ContractState.Accepted.Select((Contract c) => c.Id).ToArray()); } } [ClientRpc] private void SyncAcceptedClientRpc(int[] acceptedIds) { if (!((NetworkBehaviour)this).IsServer) { ContractState.RebuildAccepted(acceptedIds); } } public void PushProgress(int contractId, int progress) { if (((NetworkBehaviour)this).IsServer) { UpdateProgressClientRpc(contractId, progress); } } [ClientRpc] private void UpdateProgressClientRpc(int contractId, int progress) { Contract contract = ContractState.Accepted.FirstOrDefault((Contract x) => x.Id == contractId); if (contract != null) { contract.Progress = progress; } } [ServerRpc(RequireOwnership = false)] public void ReportScanServerRpc(ulong enemyNetId, string enemyName) { foreach (ObservationContract item in ContractState.Accepted.OfType<ObservationContract>()) { if (!(item.EnemyName != enemyName) && item.Scanned.Add(enemyNetId)) { item.Progress = item.Scanned.Count; PushProgress(item.Id, item.Progress); LethalContracts.Logger.LogInfo((object)$"Scan counted: {enemyName} {item.Progress}/{item.Goal}"); } } } [ServerRpc(RequireOwnership = false)] private void RequestStateServerRpc(ulong clientId) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) (int[] PoolIndices, int[] Goals) tuple = ContractState.ToSync(); int[] item = tuple.PoolIndices; int[] item2 = tuple.Goals; ClientRpcParams rpcParams = new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = new ulong[1] { clientId } } }; SendStateClientRpc(item, item2, ContractState.Accepted.Select((Contract c) => c.Id).ToArray(), rpcParams); } [ClientRpc] private void SendStateClientRpc(int[] poolIndices, int[] goals, int[] acceptedIds, ClientRpcParams rpcParams = default(ClientRpcParams)) { ContractState.RebuildFromSync(poolIndices, goals); ContractState.RebuildAccepted(acceptedIds); } [ClientRpc] public void MarkTargetClientRpc(int contractId, ulong netId, int scrapValue) { if (ContractState.Accepted.FirstOrDefault((Contract x) => x.Id == contractId) is RetrievalContract retrievalContract) { retrievalContract.TargetNetId = netId; } if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(netId, out var value)) { GrabbableObject component = ((Component)value).GetComponent<GrabbableObject>(); if ((Object)(object)component != (Object)null) { component.scrapValue = scrapValue; } ((Component)value).gameObject.AddComponent<ContractMarker>(); } } [ClientRpc] public void ContractFulfilledClientRpc(int reward) { ContractFulfilledScreen.Instance?.Enqueue(reward); } private void Update() { if (!((NetworkBehaviour)this).IsServer || Time.time < nextCheck) { return; } nextCheck = Time.time + 0.5f; foreach (RetrievalContract item in ContractState.Accepted.OfType<RetrievalContract>()) { if (item.TargetNetId == 0L || item.Finalized) { continue; } int num = 0; if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(item.TargetNetId, out var value)) { GrabbableObject component = ((Component)value).GetComponent<GrabbableObject>(); if ((Object)(object)component != (Object)null && component.isInShipRoom) { num = 1; } } if (item.Progress != num) { item.Progress = num; PushProgress(item.Id, num); if (num == 1 && (Object)(object)value != (Object)null) { ((Component)value).GetComponent<ContractMarker>()?.Stop(); } LethalContracts.Logger.LogInfo((object)("Retrieval " + ((num == 1) ? "secured" : "removed"))); } } } } [HarmonyPatch] internal static class ContractNetworkManager { private static GameObject? prefab; [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "Start")] private static void Init() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown if (!((Object)(object)prefab != (Object)null)) { prefab = new GameObject("ContractNetworkHandler"); ((Object)prefab).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)prefab); NetworkObject obj = prefab.AddComponent<NetworkObject>(); typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(obj, 1279479368u); prefab.AddComponent<ContractNetworkHandler>(); NetworkManager.Singleton.AddNetworkPrefab(prefab); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Awake")] private static void SpawnHandler() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)prefab == (Object)null) && NetworkManager.Singleton.IsServer) { GameObject val = Object.Instantiate<GameObject>(prefab, Vector3.zero, Quaternion.identity); val.GetComponent<NetworkObject>().Spawn(false); } } } }