using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using AlliedDefenses.Config; using AlliedDefenses.Core; using AlliedDefenses.Defenses; using AlliedDefenses.NetcodePatcher; using AlliedDefenses.Networking; using AlliedDefenses.UI; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("AlliedDefenses")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.0.0")] [assembly: AssemblyInformationalVersion("0.4.0+10500bfa76dcc5896caacbaa107916fcbd1098bb")] [assembly: AssemblyProduct("AlliedDefenses")] [assembly: AssemblyTitle("AlliedDefenses")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] 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; } } } namespace AlliedDefenses { [BepInPlugin("Remilulz_91.AlliedDefenses", "AlliedDefenses", "0.4.0")] public class Plugin : BaseUnityPlugin { public const string Author = "Remilulz_91"; public const string Guid = "Remilulz_91.AlliedDefenses"; public const string Name = "AlliedDefenses"; public const string Version = "0.4.0"; private readonly Harmony _harmony = new Harmony("Remilulz_91.AlliedDefenses"); public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); try { _harmony.PatchAll(); DefenseRegistry.RegisterDefaults(); HijackTicker.EnsureExists(); Log.LogInfo((object)string.Format("{0} v{1} by {2} loaded. {3} defense module(s) active.", "AlliedDefenses", "0.4.0", "Remilulz_91", DefenseRegistry.Count)); } catch (Exception arg) { Log.LogError((object)$"Failed to apply Harmony patches: {arg}"); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "AlliedDefenses"; public const string PLUGIN_NAME = "AlliedDefenses"; public const string PLUGIN_VERSION = "0.4.0"; } } namespace AlliedDefenses.UI { public static class CommandText { private static string Keyword => ModConfig.HijackCommand.Value.Trim(); public static string Usage() { string keyword = Keyword; return "ALLIED DEFENSES\n-------------------------\n" + keyword + " : hijack one defense by its id (turret or mine, e.g. " + keyword + " U9)\n" + keyword + " turrets : list all turrets and their ids\n" + keyword + " mines : list all mines and their ids\n" + keyword + " help : how the mod works\n" + keyword + " config : show the current settings\n"; } public static string HowItWorks() { string keyword = Keyword; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("ALLIED DEFENSES - HOW IT WORKS"); stringBuilder.AppendLine("------------------------------------"); stringBuilder.AppendLine("Normally the facility defenses target YOU (the employees) and"); stringBuilder.AppendLine("ignore the monsters. This mod lets you flip that."); stringBuilder.AppendLine(""); stringBuilder.AppendLine("TURRETS:"); stringBuilder.AppendLine(" " + keyword + " turrets list all turrets and their ids."); stringBuilder.AppendLine(" " + keyword + " hijack one turret (same id you'd use to disable it)."); stringBuilder.AppendLine(" An allied turret stops shooting players and instead aims at"); stringBuilder.AppendLine(" the nearest visible enemy in range."); stringBuilder.AppendLine(""); stringBuilder.AppendLine("MINES:"); stringBuilder.AppendLine(" " + keyword + " mines list all mines and their ids."); stringBuilder.AppendLine(" " + keyword + " hijack one mine (same id you'd use to disable it)."); stringBuilder.AppendLine(" An allied mine no longer explodes under players; it detonates"); stringBuilder.AppendLine(" only when an enemy steps close to it."); stringBuilder.AppendLine(""); stringBuilder.AppendLine("All hijacks last for a set time, then the defense turns hostile"); stringBuilder.AppendLine("again. Everyone in the lobby must have the mod; effects are synced."); stringBuilder.AppendLine(""); stringBuilder.AppendLine("Type " + keyword + " config to see the exact current settings."); return stringBuilder.ToString(); } public static string CurrentConfig() { float value = ModConfig.HijackDuration.Value; string text = ((value > 0f) ? $"{value:0} seconds, then it turns hostile again" : "unlimited (stays allied until end of round)"); bool value2 = ModConfig.IgnorePlayersWhenAllied.Value; int value3 = ModConfig.HijackCreditCost.Value; string text2 = ((value3 > 0) ? $"{value3} credits per hijack" : "free"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("ALLIED DEFENSES - CURRENT CONFIG"); stringBuilder.AppendLine("------------------------------------"); stringBuilder.AppendLine("Command keyword : " + Keyword); stringBuilder.AppendLine("Allied duration : " + text); stringBuilder.AppendLine($"Turret detect range : {ModConfig.EnemyDetectionRange.Value:0} meters"); stringBuilder.AppendLine($"Mine trigger radius : {ModConfig.MineTriggerRadius.Value:0} meters"); stringBuilder.AppendLine("Players can be hit : " + (value2 ? "NO - allied defenses never hurt players" : "yes - friendly fire is ON")); stringBuilder.AppendLine("Hijack cost : " + text2); stringBuilder.AppendLine("Allied colour cue : " + (ModConfig.ColorAlliedDefenses.Value ? "ON (green laser/light in dungeon, blue code on radar)" : "off")); stringBuilder.AppendLine(""); stringBuilder.AppendLine($"Defense types supported : {DefenseRegistry.Count}"); stringBuilder.AppendLine($"Currently hijacked : {HijackManager.ActiveCount}"); stringBuilder.AppendLine(""); stringBuilder.AppendLine("(Settings live in BepInEx/config/Remilulz_91.AlliedDefenses.cfg)"); return stringBuilder.ToString(); } } } namespace AlliedDefenses.Patches { [HarmonyPatch(typeof(Landmine))] public static class MinePatches { [HarmonyPrefix] [HarmonyPatch("OnTriggerEnter")] public static bool OnTriggerEnterPrefix(Landmine __instance, Collider other) { if (ShouldProtectPlayers(__instance) && ((Component)other).CompareTag("Player")) { return false; } return true; } [HarmonyPrefix] [HarmonyPatch("OnTriggerExit")] public static bool OnTriggerExitPrefix(Landmine __instance, Collider other) { if (ShouldProtectPlayers(__instance) && ((Component)other).CompareTag("Player")) { return false; } return true; } private static bool ShouldProtectPlayers(Landmine mine) { if (ModConfig.IgnorePlayersWhenAllied.Value) { return HijackManager.IsAllied((Component)(object)mine); } return false; } } [HarmonyPatch(typeof(Terminal))] public static class TerminalPatches { [HarmonyPrefix] [HarmonyPatch("ParsePlayerSentence")] public static bool ParsePrefix(Terminal __instance, ref TerminalNode __result) { string typedText = GetTypedText(__instance); if (string.IsNullOrWhiteSpace(typedText)) { return true; } typedText = typedText.Trim(); string text = ModConfig.HijackCommand.Value.Trim(); bool flag = typedText.Equals(text, StringComparison.OrdinalIgnoreCase); bool flag2 = typedText.StartsWith(text + " ", StringComparison.OrdinalIgnoreCase); if (!flag && !flag2) { return true; } string text2 = (flag ? "" : typedText.Substring(text.Length).Trim()); string typeId; string text3 = (string.IsNullOrEmpty(text2) ? CommandText.Usage() : ((!text2.Equals("help", StringComparison.OrdinalIgnoreCase) && !text2.Equals("howitworks", StringComparison.OrdinalIgnoreCase) && !text2.Equals("info", StringComparison.OrdinalIgnoreCase)) ? ((text2.Equals("config", StringComparison.OrdinalIgnoreCase) || text2.Equals("settings", StringComparison.OrdinalIgnoreCase)) ? CommandText.CurrentConfig() : ((!TryMatchGroup(text2, out typeId)) ? HijackManager.RequestHijack(text2) : HijackManager.ListDefenses(typeId))) : CommandText.HowItWorks())); __result = MakeNode(text3); return false; } private static bool TryMatchGroup(string arg, out string typeId) { string text = arg.Trim().ToLowerInvariant(); foreach (IHijackableDefense item in DefenseRegistry.All) { string text2 = item.TypeId.ToLowerInvariant(); string text3 = item.DisplayName.ToLowerInvariant(); if (text == text2 || text == text2 + "s" || text == text3 || text == text3 + "s") { typeId = item.TypeId; return true; } } typeId = ""; return false; } private static string GetTypedText(Terminal terminal) { try { object value = Traverse.Create((object)terminal).Field("screenText").GetValue(); string text = Traverse.Create(value).Property("text", (object[])null).GetValue() ?? ""; int value2 = Traverse.Create((object)terminal).Field("textAdded").GetValue(); if (value2 <= 0 || value2 > text.Length) { return ""; } return text.Substring(text.Length - value2); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read terminal input: " + ex.Message)); return ""; } } private static TerminalNode MakeNode(string text) { TerminalNode val = ScriptableObject.CreateInstance(); val.displayText = text + "\n\n"; val.clearPreviousText = true; val.maxCharactersToType = 50; return val; } } [HarmonyPatch(typeof(Turret))] public static class TurretPatches { private static readonly TurretHijack _module = new TurretHijack(); [HarmonyPrefix] [HarmonyPatch("Update")] public static bool UpdatePrefix(Turret __instance) { if (!HijackManager.IsAllied((Component)(object)__instance)) { return true; } _module.DriveAlliedTurret(__instance); return false; } } } namespace AlliedDefenses.Networking { public class HijackNetworker : NetworkBehaviour { private static bool _warnedRpc; public static HijackNetworker? Instance { get; private set; } public static HijackNetworker? Active { get { if ((Object)(object)Instance != (Object)null) { return Instance; } Instance = Object.FindObjectOfType(); return Instance; } } public override void OnNetworkSpawn() { Instance = this; ((NetworkBehaviour)this).OnNetworkSpawn(); Plugin.Log.LogInfo((object)"HijackNetworker ready (network active)."); } public override void OnNetworkDespawn() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } ((NetworkBehaviour)this).OnNetworkDespawn(); } private static void Safe(Action rpc) { try { rpc(); } catch (Exception ex) { if (!_warnedRpc) { _warnedRpc = true; Plugin.Log.LogWarning((object)("Networking note: an RPC could not be sent. This is harmless in solo and if the build isn't fully netcode-patched; multiplayer sync may be limited. (" + ex.Message + ")")); } } } public void RequestHijack(ulong netId, string typeId) { ApplyHijack(netId, typeId, allied: true); } public void RequestUnhijack(ulong netId, string typeId) { ApplyHijack(netId, typeId, allied: false); } private void ApplyHijack(ulong netId, string typeId, bool allied) { if (((NetworkBehaviour)this).IsServer) { HijackManager.ApplyHijack(netId, typeId, allied); Safe(delegate { ApplyHijackClientRpc(netId, typeId, allied); }); } else { Safe(delegate { RequestHijackServerRpc(netId, typeId, allied); }); } } [ServerRpc(RequireOwnership = false)] private void RequestHijackServerRpc(ulong netId, string typeId, bool allied) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_006d: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)base.__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost)) { ServerRpcParams val2 = default(ServerRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendServerRpc(946746885u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, netId); bool flag = typeId != null; ((FastBufferWriter)(ref val)).WriteValueSafe(ref flag, default(ForPrimitives)); if (flag) { ((FastBufferWriter)(ref val)).WriteValueSafe(typeId, false); } ((FastBufferWriter)(ref val)).WriteValueSafe(ref allied, default(ForPrimitives)); ((NetworkBehaviour)this).__endSendServerRpc(ref val, 946746885u, val2, (RpcDelivery)0); } if ((int)base.__rpc_exec_stage == 1 && (networkManager.IsServer || networkManager.IsHost)) { base.__rpc_exec_stage = (__RpcExecStage)0; ulong netId2 = netId; string typeId2 = typeId; bool allied2 = allied; HijackManager.ApplyHijack(netId2, typeId2, allied2); Safe(delegate { ApplyHijackClientRpc(netId2, typeId2, allied2); }); } } [ClientRpc] private void ApplyHijackClientRpc(ulong netId, string typeId, bool allied) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_006d: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)base.__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(4088680816u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, netId); bool flag = typeId != null; ((FastBufferWriter)(ref val)).WriteValueSafe(ref flag, default(ForPrimitives)); if (flag) { ((FastBufferWriter)(ref val)).WriteValueSafe(typeId, false); } ((FastBufferWriter)(ref val)).WriteValueSafe(ref allied, default(ForPrimitives)); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 4088680816u, val2, (RpcDelivery)0); } if ((int)base.__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { base.__rpc_exec_stage = (__RpcExecStage)0; if (!((NetworkBehaviour)this).IsServer) { HijackManager.ApplyHijack(netId, typeId, allied); } } } protected override void __initializeVariables() { ((NetworkBehaviour)this).__initializeVariables(); } protected override void __initializeRpcs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown ((NetworkBehaviour)this).__registerRpc(946746885u, new RpcReceiveHandler(__rpc_handler_946746885), "RequestHijackServerRpc"); ((NetworkBehaviour)this).__registerRpc(4088680816u, new RpcReceiveHandler(__rpc_handler_4088680816), "ApplyHijackClientRpc"); ((NetworkBehaviour)this).__initializeRpcs(); } private static void __rpc_handler_946746885(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_00ab: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { ulong netId = default(ulong); ByteUnpacker.ReadValueBitPacked(reader, ref netId); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); string typeId = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref typeId, false); } bool allied = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref allied, default(ForPrimitives)); target.__rpc_exec_stage = (__RpcExecStage)1; ((HijackNetworker)(object)target).RequestHijackServerRpc(netId, typeId, allied); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_4088680816(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_00ab: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { ulong netId = default(ulong); ByteUnpacker.ReadValueBitPacked(reader, ref netId); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); string typeId = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref typeId, false); } bool allied = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref allied, default(ForPrimitives)); target.__rpc_exec_stage = (__RpcExecStage)1; ((HijackNetworker)(object)target).ApplyHijackClientRpc(netId, typeId, allied); target.__rpc_exec_stage = (__RpcExecStage)0; } } [MethodImpl(MethodImplOptions.NoInlining)] protected internal override string __getTypeName() { return "HijackNetworker"; } } public static class NetcodePatcher { [RuntimeInitializeOnLoadMethod] private static void Init() { Type[] types = Assembly.GetExecutingAssembly().GetTypes(); Type[] array = types; foreach (Type type in array) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo in array2) { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0 && methodInfo.Name != "Init") { methodInfo.Invoke(null, null); } } } } } [HarmonyPatch] public static class NetworkObjectManager { private static GameObject? _networkPrefab; [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "Start")] public static void RegisterPrefab() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown Plugin.Log.LogInfo((object)"NetworkObjectManager: GameNetworkManager.Start reached."); if ((Object)(object)_networkPrefab != (Object)null) { Plugin.Log.LogInfo((object)"NetworkObjectManager: prefab already created, skipping."); return; } try { if ((Object)(object)NetworkManager.Singleton == (Object)null) { Plugin.Log.LogError((object)"NetworkObjectManager: NetworkManager.Singleton is null; cannot register prefab."); return; } _networkPrefab = new GameObject("AlliedDefensesNetworkHandler"); Object.DontDestroyOnLoad((Object)(object)_networkPrefab); ((Object)_networkPrefab).hideFlags = (HideFlags)61; NetworkObject netObj = _networkPrefab.AddComponent(); _networkPrefab.AddComponent(); AssignStableHash(netObj, "AlliedDefenses.HijackNetworker"); NetworkManager.Singleton.AddNetworkPrefab(_networkPrefab); Plugin.Log.LogInfo((object)"NetworkObjectManager: network prefab registered."); } catch (Exception arg) { Plugin.Log.LogError((object)$"NetworkObjectManager: failed to register prefab: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Start")] public static void SpawnHandler() { try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { Plugin.Log.LogWarning((object)"NetworkObjectManager: NetworkManager null at StartOfRound.Start."); return; } if (!singleton.IsHost && !singleton.IsServer) { Plugin.Log.LogInfo((object)"NetworkObjectManager: not the host; client will receive the handler from the host."); return; } if ((Object)(object)HijackNetworker.Instance != (Object)null) { Plugin.Log.LogInfo((object)"NetworkObjectManager: handler already spawned."); return; } if ((Object)(object)_networkPrefab == (Object)null) { Plugin.Log.LogError((object)"NetworkObjectManager: prefab is null (registration failed?); cannot spawn."); return; } GameObject val = Object.Instantiate(_networkPrefab); val.GetComponent().Spawn(false); Plugin.Log.LogInfo((object)"NetworkObjectManager: HijackNetworker spawned by the host."); } catch (Exception arg) { Plugin.Log.LogError((object)$"NetworkObjectManager: failed to spawn handler: {arg}"); } } private static void AssignStableHash(NetworkObject netObj, string key) { uint hashCode = (uint)key.GetHashCode(); FieldInfo field = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { Plugin.Log.LogError((object)"NetworkObjectManager: GlobalObjectIdHash field not found; spawn will likely fail."); return; } field.SetValue(netObj, hashCode); Plugin.Log.LogInfo((object)$"NetworkObjectManager: GlobalObjectIdHash set to {hashCode}."); } } } namespace AlliedDefenses.Defenses { public class MineHijack : IHijackableDefense { public string TypeId => "mine"; public string DisplayName => "Mine"; public Type ComponentType => typeof(Landmine); public bool TryResolveByTerminalCode(string code, out Component? defense) { defense = TerminalCodeResolver.Resolve(code, typeof(Landmine)); return (Object)(object)defense != (Object)null; } public void ApplyAlliedState(Component defense, bool allied) { if (!allied) { AlliedLightTint.Apply(defense, allied: false); } } public void TickAlliedTargeting(Component defense) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)defense == (Object)null) { return; } Landmine val = (Landmine)(object)((defense is Landmine) ? defense : null); if (val == null || val.hasExploded) { return; } AlliedLightTint.Apply((Component)(object)val, allied: true); if (!((Object)(object)NetworkManager.Singleton != (Object)null) || NetworkManager.Singleton.IsServer) { EnemyAI val2 = TargetingHelper.FindBestEnemy(((Component)val).transform.position, Vector3.up, ModConfig.MineTriggerRadius.Value, 180f, requireLineOfSight: false); if (!((Object)(object)val2 == (Object)null)) { Detonate(val); } } } private static void Detonate(Landmine mine) { if (mine.hasExploded) { return; } try { Traverse.Create((object)mine).Method("TriggerMineOnLocalClientByExiting", Array.Empty()).GetValue(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to detonate allied mine: " + ex.Message)); } } } public class TurretHijack : IHijackableDefense { private static readonly Dictionary _nextFire = new Dictionary(); private static readonly Dictionary _baseDir = new Dictionary(); private const float FireInterval = 0.21f; private const int EnemyDamagePerShot = 1; private const float AlignToleranceDeg = 10f; private static bool _diagLogged; public string TypeId => "turret"; public string DisplayName => "Turret"; public Type ComponentType => typeof(Turret); public bool TryResolveByTerminalCode(string code, out Component? defense) { defense = TerminalCodeResolver.Resolve(code, typeof(Turret)); return (Object)(object)defense != (Object)null; } public void ApplyAlliedState(Component defense, bool allied) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) Turret val = (Turret)(object)((defense is Turret) ? defense : null); if (val == null) { return; } if (allied) { val.turretActive = true; val.turretMode = (TurretMode)0; if ((Object)(object)val.turretAnimator != (Object)null) { val.turretAnimator.SetInteger("TurretMode", 0); } TurretVisuals.SetAllied(val, allied: true); if ((Object)(object)val.aimPoint != (Object)null && (Object)(object)val.centerPoint != (Object)null) { Dictionary baseDir = _baseDir; int instanceID = ((Object)val).GetInstanceID(); Vector3 val2 = val.aimPoint.position - val.centerPoint.position; baseDir[instanceID] = ((Vector3)(ref val2)).normalized; } LogHierarchyOnce(val); } else { int instanceID2 = ((Object)val).GetInstanceID(); _nextFire.Remove(instanceID2); _baseDir.Remove(instanceID2); TurretVisuals.SetAllied(val, allied: false); } } public void TickAlliedTargeting(Component defense) { } public void DriveAlliedTurret(Turret turret) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) if (!GetNodes(turret, out Transform rod, out Transform pivot, out Transform muzzle)) { return; } Vector3 val = muzzle.position - pivot.position; Vector3 normalized = ((Vector3)(ref val)).normalized; EnemyAI val2 = TargetingHelper.FindBestEnemy(muzzle.position, normalized, ModConfig.EnemyDetectionRange.Value); if ((Object)(object)val2 == (Object)null) { IdleScan(turret, rod, pivot, muzzle); TurretVisuals.HideBeam(turret); return; } Vector3 val3 = ((Component)val2).transform.position + Vector3.up * 0.5f; float degPerSec = Mathf.Max(turret.rotationSpeed, 90f); AimRodAt(rod, pivot.position, muzzle.position, val3, degPerSec); UpdateBeam(turret, pivot, muzzle); bool flag = (Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsServer; float num = Vector3.Angle(muzzle.position - pivot.position, val3 - pivot.position); if (flag && num <= 10f) { TryFireAtEnemy(turret, val2); } } private static bool GetNodes(Turret turret, out Transform rod, out Transform pivot, out Transform muzzle) { rod = turret.turretRod; pivot = turret.centerPoint; muzzle = turret.aimPoint; if ((Object)(object)rod != (Object)null && (Object)(object)pivot != (Object)null) { return (Object)(object)muzzle != (Object)null; } return false; } private static void AimRodAt(Transform rod, Vector3 pivotPos, Vector3 muzzlePos, Vector3 targetPoint, float degPerSec) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0042: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) Vector3 val = targetPoint - pivotPos; Vector3 val2 = muzzlePos - pivotPos; if (!(((Vector3)(ref val)).sqrMagnitude < 1E-05f) && !(((Vector3)(ref val2)).sqrMagnitude < 1E-05f)) { ((Vector3)(ref val)).Normalize(); ((Vector3)(ref val2)).Normalize(); Vector3 val3 = rod.InverseTransformDirection(val2); Quaternion val4 = Quaternion.LookRotation(val, Vector3.up) * Quaternion.Inverse(Quaternion.LookRotation(val3, Vector3.up)); rod.rotation = Quaternion.RotateTowards(rod.rotation, val4, degPerSec * Time.deltaTime); } } private static void IdleScan(Turret turret, Transform rod, Transform pivot, Transform muzzle) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_007d: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)turret).GetInstanceID(); Vector3 val2; if (!_baseDir.TryGetValue(instanceID, out var value)) { Vector3 val = muzzle.position - pivot.position; val2 = ((Vector3)(ref val)).normalized; } else { val2 = value; } Vector3 val3 = val2; float num = Mathf.Sin(Time.time * 0.6f) * 50f; Vector3 val4 = Quaternion.AngleAxis(num, Vector3.up) * val3; AimRodAt(rod, pivot.position, muzzle.position, pivot.position + val4 * 5f, 60f); } private static void LogHierarchyOnce(Turret turret) { if (_diagLogged) { return; } _diagLogged = true; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("[Turret diagnostic] PREFAB TREE (R=Renderer L=Light Ln=LineRenderer T=Turret):"); Transform val = ((Component)turret).transform; for (int i = 0; i < 2; i++) { if (!((Object)(object)val.parent != (Object)null)) { break; } val = val.parent; } int count = 0; DumpTree(val, 0, stringBuilder, ref count); stringBuilder.AppendLine("[Turret diagnostic] Transform fields on Turret:"); FieldInfo[] fields = typeof(Turret).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(Transform).IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(turret); Transform val2 = (Transform)((value is Transform) ? value : null); stringBuilder.AppendLine(" " + fieldInfo.Name + " -> " + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "null")); } } Plugin.Log.LogInfo((object)stringBuilder.ToString()); } private static void DumpTree(Transform t, int depth, StringBuilder sb, ref int count) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown if (count++ > 80 || depth > 5) { return; } string text = ""; if ((Object)(object)((Component)t).GetComponent() != (Object)null) { text += "R"; } if ((Object)(object)((Component)t).GetComponent() != (Object)null) { text += "L"; } if ((Object)(object)((Component)t).GetComponent() != (Object)null) { text += "Ln"; } if ((Object)(object)((Component)t).GetComponent() != (Object)null) { text += "T"; } sb.AppendLine(new string(' ', depth * 2) + ((Object)t).name + " [" + text + "]"); foreach (Transform item in t) { Transform t2 = item; DumpTree(t2, depth + 1, sb, ref count); } } private static void UpdateBeam(Turret turret, Transform pivot, Transform muzzle) { //IL_000c: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0043: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) float value = ModConfig.EnemyDetectionRange.Value; Vector3 val = muzzle.position - pivot.position; Vector3 normalized = ((Vector3)(ref val)).normalized; Vector3 position = muzzle.position; RaycastHit val2 = default(RaycastHit); Vector3 to = (Physics.Raycast(position, normalized, ref val2, value, -1, (QueryTriggerInteraction)1) ? ((RaycastHit)(ref val2)).point : (position + normalized * value)); TurretVisuals.DrawBeam(turret, position, to); } private void TryFireAtEnemy(Turret turret, EnemyAI enemy) { int instanceID = ((Object)turret).GetInstanceID(); float time = Time.time; if (!_nextFire.TryGetValue(instanceID, out var value) || !(time < value)) { _nextFire[instanceID] = time + 0.21f; enemy.HitEnemy(1, (PlayerControllerB)null, false, -1); MuzzleFlash(turret); } } private static void MuzzleFlash(Turret turret) { if ((Object)(object)turret.bulletParticles != (Object)null) { turret.bulletParticles.Emit(2); } } } } namespace AlliedDefenses.Core { public static class AlliedLightTint { private sealed class Cache { public Light[] Lights = Array.Empty(); public Color[] Colors = Array.Empty(); } private static readonly Dictionary _cache = new Dictionary(); public static void Apply(Component defense, bool allied) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.ColorAlliedDefenses.Value || (Object)(object)defense == (Object)null) { return; } int instanceID = ((Object)defense).GetInstanceID(); if (allied) { if (!_cache.TryGetValue(instanceID, out Cache value)) { Light[] componentsInChildren = defense.GetComponentsInChildren(true); Cache cache = new Cache(); cache.Lights = componentsInChildren; cache.Colors = (Color[])(object)new Color[componentsInChildren.Length]; value = cache; for (int i = 0; i < componentsInChildren.Length; i++) { value.Colors[i] = componentsInChildren[i].color; } _cache[instanceID] = value; } Color alliedColor = ModConfig.AlliedColor; Light[] lights = value.Lights; foreach (Light val in lights) { if ((Object)(object)val != (Object)null) { val.color = alliedColor; } } } else { if (!_cache.TryGetValue(instanceID, out Cache value2)) { return; } for (int k = 0; k < value2.Lights.Length; k++) { if ((Object)(object)value2.Lights[k] != (Object)null) { value2.Lights[k].color = value2.Colors[k]; } } _cache.Remove(instanceID); } } } public static class DefenseRegistry { private static readonly List _defenses = new List(); public static IReadOnlyList All => _defenses; public static int Count => _defenses.Count; public static void RegisterDefaults() { Register(new TurretHijack()); Register(new MineHijack()); } public static void Register(IHijackableDefense defense) { _defenses.Add(defense); Plugin.Log.LogInfo((object)("Defense module registered: " + defense.TypeId + " (" + defense.DisplayName + ")")); } public static bool TryResolve(string code, out IHijackableDefense? module, out Component? defense) { foreach (IHijackableDefense defense3 in _defenses) { if (defense3.TryResolveByTerminalCode(code, out Component defense2) && (Object)(object)defense2 != (Object)null) { module = defense3; defense = defense2; return true; } } module = null; defense = null; return false; } public static IHijackableDefense? FindModule(string typeId) { foreach (IHijackableDefense defense in _defenses) { if (defense.TypeId == typeId) { return defense; } } return null; } } public class HijackEntry { public ulong NetworkId; public string TypeId = ""; public Component Defense; public float ExpireTime; } public static class HijackManager { private static readonly Dictionary _active = new Dictionary(); public static int ActiveCount => _active.Count; public static bool IsAllied(ulong networkId) { return _active.ContainsKey(networkId); } public static bool IsAllied(Component defense) { ulong? num = ResolveNetworkId(defense); if (num.HasValue) { return _active.ContainsKey(num.Value); } return false; } public static HijackEntry? Get(ulong networkId) { if (!_active.TryGetValue(networkId, out HijackEntry value)) { return null; } return value; } public static string RequestHijack(string code) { if (!DefenseRegistry.TryResolve(code, out IHijackableDefense module, out Component defense) || (Object)(object)defense == (Object)null || module == null) { return "No defense found for code '" + code + "'."; } ulong? num = ResolveNetworkId(defense); if (!num.HasValue) { return "This defense has no network identity (cannot be hijacked)."; } HijackNetworker active = HijackNetworker.Active; if ((Object)(object)active == (Object)null) { return "Network handler not ready yet. Try again in a moment."; } active.RequestHijack(num.Value, module.TypeId); float value = ModConfig.HijackDuration.Value; string text = ((value > 0f) ? $"for {value:0} seconds" : "until end of round"); return "Hijacking " + module.DisplayName + " '" + code + "' " + text + "..."; } public static string ListDefenses(string typeId) { IHijackableDefense hijackableDefense = DefenseRegistry.FindModule(typeId); if (hijackableDefense == null) { return "Unknown defense type '" + typeId + "'."; } List list = new List(); Object[] array = Object.FindObjectsOfType(hijackableDefense.ComponentType); foreach (Object val in array) { Component val2 = (Component)(object)((val is Component) ? val : null); if (val2 != null) { list.Add(TerminalCodeResolver.GetCodeFor(val2)); } } list.Sort(StringComparer.OrdinalIgnoreCase); string text = hijackableDefense.DisplayName.ToLower(); if (list.Count == 0) { return "No " + text + "s on this level."; } return string.Format("{0} {1}(s) on this level:\n {2}\n", list.Count, text, string.Join(", ", list)) + "Use '" + ModConfig.HijackCommand.Value + " ' to hijack one."; } public static void ApplyHijack(ulong networkId, string typeId, bool allied) { Component val = ResolveComponent(networkId, typeId); IHijackableDefense hijackableDefense = DefenseRegistry.FindModule(typeId); if ((Object)(object)val == (Object)null || hijackableDefense == null) { Plugin.Log.LogWarning((object)$"ApplyHijack: could not resolve netId={networkId} type={typeId}"); return; } hijackableDefense.ApplyAlliedState(val, allied); if (allied) { float value = ModConfig.HijackDuration.Value; _active[networkId] = new HijackEntry { NetworkId = networkId, TypeId = typeId, Defense = val, ExpireTime = ((value > 0f) ? (Time.time + value) : 0f) }; Plugin.Log.LogInfo((object)$"{hijackableDefense.DisplayName} (net {networkId}) is now ALLIED."); } else { RadarTimerDisplay.Restore(val); _active.Remove(networkId); Plugin.Log.LogInfo((object)$"{hijackableDefense.DisplayName} (net {networkId}) is hostile again."); } } public static void Tick() { if (_active.Count == 0) { return; } bool flag = (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer; List list = null; List list2 = null; foreach (KeyValuePair item in _active) { HijackEntry value = item.Value; if ((Object)(object)value.Defense == (Object)null) { (list2 ?? (list2 = new List())).Add(value.NetworkId); continue; } try { DefenseRegistry.FindModule(value.TypeId)?.TickAlliedTargeting(value.Defense); RadarTimerDisplay.Update(value); } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Tick error for {value.TypeId} (net {value.NetworkId}); dropping it: {ex.Message}"); (list2 ?? (list2 = new List())).Add(value.NetworkId); continue; } if (flag && value.ExpireTime > 0f && Time.time >= value.ExpireTime) { (list ?? (list = new List())).Add(value); } } if (list2 != null) { foreach (ulong item2 in list2) { _active.Remove(item2); } } if (list == null) { return; } foreach (HijackEntry item3 in list) { HijackNetworker.Active?.RequestUnhijack(item3.NetworkId, item3.TypeId); } } public static void ClearAll() { foreach (HijackEntry value in _active.Values) { DefenseRegistry.FindModule(value.TypeId)?.ApplyAlliedState(value.Defense, allied: false); RadarTimerDisplay.Restore(value.Defense); } _active.Clear(); } public static ulong? ResolveNetworkId(Component c) { NetworkObject componentInParent = c.GetComponentInParent(); if (!((Object)(object)componentInParent != (Object)null)) { return null; } return componentInParent.NetworkObjectId; } private static Component? ResolveComponent(ulong networkId, string typeId) { NetworkManager singleton = NetworkManager.Singleton; NetworkSpawnManager val = ((singleton != null) ? singleton.SpawnManager : null); if (val == null) { return null; } if (!val.SpawnedObjects.TryGetValue(networkId, out var value) || (Object)(object)value == (Object)null) { return null; } IHijackableDefense hijackableDefense = DefenseRegistry.FindModule(typeId); if (hijackableDefense == null) { return null; } return ((Component)value).GetComponentInChildren(hijackableDefense.ComponentType); } } public class HijackTicker : MonoBehaviour { public static HijackTicker? Instance { get; private set; } public static void EnsureExists() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("AlliedDefenses_Ticker"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; Instance = val.AddComponent(); } } private void Update() { HijackManager.Tick(); } } public interface IHijackableDefense { string TypeId { get; } string DisplayName { get; } Type ComponentType { get; } bool TryResolveByTerminalCode(string code, out Component? defense); void ApplyAlliedState(Component defense, bool allied); void TickAlliedTargeting(Component defense); } public static class RadarTimerDisplay { private static readonly Dictionary _originalText = new Dictionary(); private static readonly Dictionary _originalColor = new Dictionary(); public static void Update(HijackEntry entry) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) TerminalAccessibleObject val = FindAccessible(entry.Defense); if ((Object)(object)val == (Object)null) { return; } Component radarText = GetRadarText(val); if (!((Object)(object)radarText == (Object)null)) { int instanceID = ((Object)val).GetInstanceID(); if (!_originalText.ContainsKey(instanceID)) { _originalText[instanceID] = StripTimer(GetText(radarText)); _originalColor[instanceID] = GetColor(radarText); } string text = _originalText[instanceID]; string text2 = ((!(entry.ExpireTime <= 0f)) ? Format(Mathf.Max(0f, entry.ExpireTime - Time.time)) : "ALLY"); SetText(radarText, text + "\n" + text2); if (ModConfig.ColorAlliedDefenses.Value) { SetColor(radarText, ModConfig.RadarAlliedColor); } } } public static void Restore(Component defense) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) TerminalAccessibleObject val = FindAccessible(defense); if ((Object)(object)val == (Object)null) { return; } int instanceID = ((Object)val).GetInstanceID(); Component radarText = GetRadarText(val); if ((Object)(object)radarText != (Object)null) { if (_originalText.TryGetValue(instanceID, out string value)) { SetText(radarText, value); } if (_originalColor.TryGetValue(instanceID, out var value2)) { SetColor(radarText, value2); } } _originalText.Remove(instanceID); _originalColor.Remove(instanceID); } private static string Format(float seconds) { int num = Mathf.CeilToInt(seconds); return $"{num / 60}:{num % 60:00}"; } private static string StripTimer(string text) { if (string.IsNullOrEmpty(text)) { return text ?? ""; } int num = text.IndexOf('\n'); if (num < 0) { return text; } return text.Substring(0, num); } private static TerminalAccessibleObject? FindAccessible(Component defense) { if ((Object)(object)defense == (Object)null) { return null; } return defense.GetComponentInParent() ?? defense.GetComponentInChildren(); } private static Component? GetRadarText(TerminalAccessibleObject tao) { string[] array = new string[3] { "mapRadarText", "radarText", "codeText" }; foreach (string text in array) { try { Component value = Traverse.Create((object)tao).Field(text).GetValue(); if ((Object)(object)value != (Object)null) { return value; } } catch { } } return null; } private static string GetText(Component tmp) { try { return Traverse.Create((object)tmp).Property("text", (object[])null).GetValue() ?? ""; } catch { return ""; } } private static void SetText(Component tmp, string value) { try { Traverse.Create((object)tmp).Property("text", (object[])null).SetValue((object)value); } catch { } } private static Color GetColor(Component tmp) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) try { return Traverse.Create((object)tmp).Property("color", (object[])null).GetValue(); } catch { return Color.white; } } private static void SetColor(Component tmp, Color value) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Traverse.Create((object)tmp).Property("color", (object[])null).SetValue((object)value); } catch { } } } public static class TargetingHelper { private const int LineOfSightMask = 1051400; public static float Range => ModConfig.EnemyDetectionRange.Value; public static EnemyAI? FindBestEnemy(Vector3 origin, Vector3 forward, float range, float coneHalfAngle = 180f, bool requireLineOfSight = true) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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) RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null) { return null; } EnemyAI result = null; float num = float.MaxValue; float num2 = range * range; foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if (!((Object)(object)spawnedEnemy == (Object)null) && !spawnedEnemy.isEnemyDead) { Vector3 position = ((Component)spawnedEnemy).transform.position; Vector3 val = position - origin; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude > num2) && (!(coneHalfAngle < 180f) || !(Vector3.Angle(forward, val) > coneHalfAngle)) && (!requireLineOfSight || HasLineOfSight(origin, position)) && sqrMagnitude < num) { num = sqrMagnitude; result = spawnedEnemy; } } } return result; } public static bool HasLineOfSight(Vector3 from, Vector3 to) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_001d: Unknown result type (might be due to invalid IL or missing references) Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.01f) { return true; } return !Physics.Raycast(from, ((Vector3)(ref val)).normalized, magnitude - 0.5f, 1051400, (QueryTriggerInteraction)1); } } public static class TerminalCodeResolver { public static Component? Resolve(string code, Type componentType) { if (string.IsNullOrEmpty(code)) { return null; } TerminalAccessibleObject[] array = Object.FindObjectsOfType(); foreach (TerminalAccessibleObject val in array) { string a = ReadObjectCode(val); if (string.Equals(a, code, StringComparison.OrdinalIgnoreCase)) { Component val2 = ((Component)val).GetComponentInChildren(componentType) ?? ((Component)val).GetComponentInParent(componentType); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } public static string GetCodeFor(Component c) { if ((Object)(object)c == (Object)null) { return "?"; } TerminalAccessibleObject val = c.GetComponentInParent() ?? c.GetComponentInChildren(); string text = (((Object)(object)val != (Object)null) ? ReadObjectCode(val) : ""); if (!string.IsNullOrEmpty(text)) { return text; } return "?"; } public static string ReadObjectCode(TerminalAccessibleObject acc) { string[] array = new string[3] { "objectCode", "codeString", "code" }; foreach (string text in array) { try { string value = Traverse.Create((object)acc).Field(text).GetValue(); if (!string.IsNullOrEmpty(value)) { return value; } } catch { } } return ""; } } public static class TurretVisuals { private sealed class Cache { public LineRenderer[] Lines = Array.Empty(); public Color[] LineStart = Array.Empty(); public Color[] LineEnd = Array.Empty(); public Light[] Lights = Array.Empty(); public Color[] LightColors = Array.Empty(); } private static readonly Dictionary _cache = new Dictionary(); private static readonly Dictionary _beams = new Dictionary(); public static void SetAllied(Turret turret, bool allied) { //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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_010f: 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) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.ColorAlliedDefenses.Value) { return; } int instanceID = ((Object)turret).GetInstanceID(); if (allied) { if (!_cache.ContainsKey(instanceID)) { _cache[instanceID] = Capture(turret); } Color alliedColor = ModConfig.AlliedColor; Cache cache = _cache[instanceID]; LineRenderer[] lines = cache.Lines; foreach (LineRenderer val in lines) { if (!((Object)(object)val == (Object)null)) { val.startColor = alliedColor; val.endColor = alliedColor; } } Light[] lights = cache.Lights; foreach (Light val2 in lights) { if (!((Object)(object)val2 == (Object)null)) { val2.color = alliedColor; } } } else { if (!_cache.TryGetValue(instanceID, out Cache value)) { return; } for (int k = 0; k < value.Lines.Length; k++) { if (!((Object)(object)value.Lines[k] == (Object)null)) { value.Lines[k].startColor = value.LineStart[k]; value.Lines[k].endColor = value.LineEnd[k]; } } for (int l = 0; l < value.Lights.Length; l++) { if (!((Object)(object)value.Lights[l] == (Object)null)) { value.Lights[l].color = value.LightColors[l]; } } _cache.Remove(instanceID); if (_beams.TryGetValue(instanceID, out LineRenderer value2)) { if ((Object)(object)value2 != (Object)null) { Object.Destroy((Object)(object)((Component)value2).gameObject); } _beams.Remove(instanceID); } } } public static void DrawBeam(Turret turret, Vector3 from, Vector3 to) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.ColorAlliedDefenses.Value) { int instanceID = ((Object)turret).GetInstanceID(); if (!_beams.TryGetValue(instanceID, out LineRenderer value) || (Object)(object)value == (Object)null) { value = CreateBeam(turret); _beams[instanceID] = value; } ((Renderer)value).enabled = true; value.useWorldSpace = true; value.positionCount = 2; value.SetPosition(0, from); value.SetPosition(1, to); } } public static void HideBeam(Turret turret) { if (_beams.TryGetValue(((Object)turret).GetInstanceID(), out LineRenderer value) && (Object)(object)value != (Object)null) { ((Renderer)value).enabled = false; } } private static LineRenderer CreateBeam(Turret turret) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0054: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0054: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("AlliedDefenses_Beam"); val.transform.SetParent(((Component)turret).transform, false); LineRenderer val2 = val.AddComponent(); LineRenderer componentInChildren = ((Component)turret).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((Renderer)componentInChildren).sharedMaterial != (Object)null) { ((Renderer)val2).material = new Material(((Renderer)componentInChildren).sharedMaterial); } Color color = (val2.endColor = (val2.startColor = ModConfig.AlliedColor)); try { if ((Object)(object)((Renderer)val2).material != (Object)null) { ((Renderer)val2).material.color = color; } } catch { } val2.startWidth = (((Object)(object)componentInChildren != (Object)null) ? componentInChildren.startWidth : 0.04f); val2.endWidth = (((Object)(object)componentInChildren != (Object)null) ? componentInChildren.endWidth : 0.04f); val2.numCapVertices = 2; ((Renderer)val2).enabled = false; return val2; } private static Cache Capture(Turret turret) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) LineRenderer[] componentsInChildren = ((Component)turret).GetComponentsInChildren(true); Light[] componentsInChildren2 = ((Component)turret).GetComponentsInChildren(true); Cache cache = new Cache(); cache.Lines = componentsInChildren; cache.LineStart = (Color[])(object)new Color[componentsInChildren.Length]; cache.LineEnd = (Color[])(object)new Color[componentsInChildren.Length]; cache.Lights = componentsInChildren2; cache.LightColors = (Color[])(object)new Color[componentsInChildren2.Length]; Cache cache2 = cache; for (int i = 0; i < componentsInChildren.Length; i++) { cache2.LineStart[i] = componentsInChildren[i].startColor; cache2.LineEnd[i] = componentsInChildren[i].endColor; } for (int j = 0; j < componentsInChildren2.Length; j++) { cache2.LightColors[j] = componentsInChildren2[j].color; } return cache2; } } } namespace AlliedDefenses.Config { public static class ModConfig { public static ConfigEntry HijackCommand; public static ConfigEntry HijackDuration; public static ConfigEntry EnemyDetectionRange; public static ConfigEntry MineTriggerRadius; public static ConfigEntry IgnorePlayersWhenAllied; public static ConfigEntry HijackCreditCost; public static ConfigEntry ColorAlliedDefenses; public static ConfigEntry AlliedColorHex; public static ConfigEntry RadarAlliedColorHex; public static Color AlliedColor => ParseHex(AlliedColorHex.Value, Color.green); public static Color RadarAlliedColor => ParseHex(RadarAlliedColorHex.Value, new Color(0.118f, 0.565f, 1f)); public static void Init(ConfigFile cfg) { HijackCommand = cfg.Bind("General", "HijackCommand", "ally", "Keyword typed in the terminal. In-game usage: (e.g. ally A0)"); HijackDuration = cfg.Bind("General", "HijackDuration", 60f, "How many seconds a defense stays allied. Set to 0 for unlimited."); EnemyDetectionRange = cfg.Bind("Targeting", "EnemyDetectionRange", 30f, "Maximum distance (m) at which a hijacked turret detects an enemy."); MineTriggerRadius = cfg.Bind("Targeting", "MineTriggerRadius", 4f, "Radius (m) within which an allied mine detonates on a nearby enemy."); IgnorePlayersWhenAllied = cfg.Bind("Targeting", "IgnorePlayersWhenAllied", true, "If true, an allied defense never fires at a player (recommended)."); HijackCreditCost = cfg.Bind("Economy", "HijackCreditCost", 0, "Credit cost to hijack a defense. 0 = free."); ColorAlliedDefenses = cfg.Bind("Visuals", "ColorAlliedDefenses", true, "Tint allied defenses (turret laser/light and the radar code) so you can tell they're on your side."); AlliedColorHex = cfg.Bind("Visuals", "AlliedColorHex", "00FF00", "In-world color for allied turret laser/light, HTML hex without '#'. Default 00FF00 (green)."); RadarAlliedColorHex = cfg.Bind("Visuals", "RadarAlliedColorHex", "1E90FF", "Radar-map color for allied defenses, HTML hex without '#'. Default 1E90FF (blue). Avoid green here: the game already uses green for active codes."); } private static Color ParseHex(string hex, Color fallback) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); if (!ColorUtility.TryParseHtmlString("#" + hex, ref result)) { return fallback; } return result; } } } namespace __GEN { internal class NetworkVariableSerializationHelper { [RuntimeInitializeOnLoadMethod] internal static void InitializeSerialization() { } } } namespace AlliedDefenses.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }