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 Terminal that can kill v1.5.0
BepInEx/plugins/Solon.TerminalThatCanKill.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalDoors.Doors; using LethalDoors.Patches; using LethalDoors.Traps; 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: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Solon")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.5.0.0")] [assembly: AssemblyInformationalVersion("1.5.0+4141c7414e36aec995604d620adf2aac05b22e28")] [assembly: AssemblyProduct("Terminal that can kill")] [assembly: AssemblyTitle("Solon.TerminalThatCanKill")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.5.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LethalDoors { internal static class GameCompat { private const int TurretModeBerserk = 3; public const int HijackSignal = 424242; public static bool IsHost { get { if ((Object)(object)NetworkManager.Singleton != (Object)null) { if (!NetworkManager.Singleton.IsHost) { return NetworkManager.Singleton.IsServer; } return true; } return false; } } public static PlayerControllerB LocalPlayer { get { if (!((Object)(object)GameNetworkManager.Instance != (Object)null)) { return null; } return GameNetworkManager.Instance.localPlayerController; } } public static int LocalPlayerId { get { PlayerControllerB localPlayer = LocalPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return -1; } return (int)localPlayer.playerClientId; } } public static bool IsPlayerAlive(PlayerControllerB p) { if ((Object)(object)p != (Object)null && p.isPlayerControlled) { return !p.isPlayerDead; } return false; } public static void KillLocalPlayer(PlayerControllerB player, int deathAnimation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_001c: Unknown result type (might be due to invalid IL or missing references) try { player.KillPlayer(Vector3.up * 14f, true, (CauseOfDeath)17, deathAnimation, default(Vector3), false); } catch (Exception arg) { Plugin.Log.LogError((object)$"KillLocalPlayer failed: {arg}"); } } public static void KillEnemy(EnemyAI enemy) { try { enemy.KillEnemyOnOwnerClient(true); } catch (Exception) { try { enemy.KillEnemyServerRpc(true); } catch (Exception arg) { Plugin.Log.LogError((object)$"KillEnemy failed: {arg}"); } } } public static bool IsEnemyDead(EnemyAI enemy) { try { return (Object)(object)enemy == (Object)null || enemy.isEnemyDead; } catch { return (Object)(object)enemy == (Object)null; } } public static string EnemyName(EnemyAI enemy) { try { if ((Object)(object)enemy != (Object)null && (Object)(object)enemy.enemyType != (Object)null && !string.IsNullOrEmpty(enemy.enemyType.enemyName)) { return enemy.enemyType.enemyName; } } catch { } if (!((Object)(object)enemy != (Object)null)) { return "unknown"; } return ((object)enemy).GetType().Name; } public static void DetonateMine(Landmine mine) { if ((Object)(object)mine == (Object)null) { return; } try { if (!mine.hasExploded) { mine.ExplodeMineServerRpc(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"DetonateMine failed: {arg}"); } } public static void BerserkTurret(Turret turret) { if ((Object)(object)turret == (Object)null) { return; } try { turret.turretActive = true; turret.SwitchTurretMode(3); turret.EnterBerserkModeServerRpc(LocalPlayerId); } catch (Exception arg) { Plugin.Log.LogError((object)$"BerserkTurret failed: {arg}"); } } public static bool IsBerserk(Turret turret) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 try { return (Object)(object)turret != (Object)null && (int)turret.turretMode == 3; } catch { return false; } } public static void SustainBerserk(Turret turret, float minTimer) { if ((Object)(object)turret == (Object)null || !IsHost) { return; } try { if (!turret.enteringBerserkMode && IsBerserk(turret)) { turret.berserkTimer = Mathf.Max(turret.berserkTimer, minTimer); } } catch { } } public static void DisableTurret(Turret turret) { if ((Object)(object)turret == (Object)null) { return; } try { turret.ToggleTurretEnabled(false); } catch (Exception arg) { Plugin.Log.LogError((object)$"DisableTurret failed: {arg}"); } } public static void DisableMineQuiet(Landmine mine) { if ((Object)(object)mine == (Object)null) { return; } try { mine.ToggleMine(false); } catch (Exception arg) { Plugin.Log.LogError((object)$"DisableMineQuiet failed: {arg}"); } } public static void BroadcastHijack(Landmine mine) { if (!((Object)(object)mine == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(MineHijackBlink(mine)); } } private static IEnumerator MineHijackBlink(Landmine mine) { try { mine.ToggleMine(false); } catch { yield break; } yield return null; if ((Object)(object)mine != (Object)null) { try { mine.ToggleMine(true); } catch { } } } public static bool IsFiring(Turret turret) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 try { return (Object)(object)turret != (Object)null && (int)turret.turretMode == 2; } catch { return false; } } public static void EngageTurret(Turret turret) { if ((Object)(object)turret == (Object)null) { return; } try { if ((Object)(object)turret.targetPlayerWithRotation == (Object)null) { turret.targetPlayerWithRotation = LocalPlayer; } } catch { } } public static void DisengageTurret(Turret turret) { if ((Object)(object)turret == (Object)null) { return; } try { turret.targetPlayerWithRotation = null; } catch { } } public static void BroadcastHijack(Turret turret) { if ((Object)(object)turret == (Object)null) { return; } try { turret.EnterBerserkModeServerRpc(424242); } catch (Exception arg) { Plugin.Log.LogError((object)$"BroadcastHijack failed: {arg}"); } } public static void CancelBerserk(Turret turret) { if ((Object)(object)turret == (Object)null) { return; } try { if (IsBerserk(turret)) { turret.SwitchTurretMode(0); } } catch { } } public static void PlayHackCue(Turret turret) { if (!((Object)(object)turret == (Object)null) && Plugin.Config.AlliedHackSound.Value && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(HackCue(turret.mainAudio, turret.turretActivate)); } } public static void PlayHackCue(Landmine mine) { if (!((Object)(object)mine == (Object)null) && Plugin.Config.AlliedHackSound.Value && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(HackCue(mine.mineAudio, mine.minePress)); } } private static IEnumerator HackCue(AudioSource source, AudioClip clip) { yield return (object)new WaitForSeconds(0.35f); if (!((Object)(object)source == (Object)null) && !((Object)(object)clip == (Object)null)) { float pitch = source.pitch; try { source.pitch = 1.25f; source.PlayOneShot(clip, 0.9f); WalkieTalkie.TransmitOneShotAudio(source, clip, 1f); } catch { } yield return (object)new WaitForSeconds(0.12f); if ((Object)(object)source != (Object)null) { source.pitch = pitch; } } } public static void HurtEnemy(EnemyAI enemy, int force) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemy == (Object)null || !IsHost) { return; } try { if (!((Object)(object)enemy.enemyType != (Object)null) || enemy.enemyType.canDie) { enemy.HitEnemyOnLocalClient(force, Vector3.zero, (PlayerControllerB)null, true, -1); } } catch (Exception arg) { Plugin.Log.LogError((object)$"HurtEnemy failed: {arg}"); } } public static bool HasLineOfSight(Vector3 from, Vector3 to) { //IL_001b: 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) try { int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : (-1)); return !Physics.Linecast(from, to, num, (QueryTriggerInteraction)1); } catch { return true; } } public static Vector3 DoorCenter(Component door) { //IL_0009: 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_0021: 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_0029: 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_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)door == (Object)null) { return Vector3.zero; } try { Collider componentInChildren = door.GetComponentInChildren<Collider>(); if ((Object)(object)componentInChildren != (Object)null) { Bounds bounds = componentInChildren.bounds; return ((Bounds)(ref bounds)).center; } } catch { } return door.transform.position; } } public enum AffectedDoors { ShipDoor, TerminalDoors, Both } public enum DamageMode { InstantKill, DamageOverTime } internal sealed class ModConfig { public readonly ConfigEntry<bool> VerboseLogging; public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> KillPlayers; public readonly ConfigEntry<bool> KillMonsters; public readonly ConfigEntry<AffectedDoors> AffectedDoors; public readonly ConfigEntry<float> SafeZoneSeconds; public readonly ConfigEntry<DamageMode> DamageMode; public readonly ConfigEntry<int> DamageAmount; public readonly ConfigEntry<float> ShipDoorCloseSeconds; public readonly ConfigEntry<float> TerminalDoorCloseSeconds; public readonly ConfigEntry<float> DoorwayThickness; public readonly ConfigEntry<float> DoorwayWidth; public readonly ConfigEntry<float> DoorwayHeight; public readonly ConfigEntry<int> PlayerDeathAnimationId; public readonly ConfigEntry<string> ExcludedEnemies; public readonly ConfigEntry<bool> EnableRemoteControl; public readonly ConfigEntry<float> MineErrorChance; public readonly ConfigEntry<float> TurretErrorChance; public readonly ConfigEntry<float> MineErrorDamageMultiplier; public readonly ConfigEntry<float> TurretRampageDuration; public readonly ConfigEntry<bool> TurretAlwaysBerserk; public readonly ConfigEntry<bool> MineAlwaysDetonate; public readonly ConfigEntry<bool> TurretFlipOnBerserkExit; public readonly ConfigEntry<float> TurretFlipSmoothDuration; public readonly ConfigEntry<bool> EnableAlliedTraps; public readonly ConfigEntry<float> AlliedChance; public readonly ConfigEntry<float> AlliedDuration; public readonly ConfigEntry<float> AlliedTurretRange; public readonly ConfigEntry<float> AlliedMineTriggerRadius; public readonly ConfigEntry<bool> AlliedHackSound; public readonly ConfigEntry<bool> AlliedTint; public readonly ConfigEntry<bool> MiniTurretAlwaysAllied; public readonly ConfigEntry<float> MineErrorChainRadius; public readonly ConfigEntry<bool> MineWarnBeforeDetonate; public ModConfig(ConfigFile cfg) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Expected O, but got Unknown //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Expected O, but got Unknown //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Expected O, but got Unknown //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Expected O, but got Unknown //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Expected O, but got Unknown //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Expected O, but got Unknown //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Expected O, but got Unknown //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Expected O, but got Unknown //IL_04bb: Unknown result type (might be due to invalid IL or missing references) //IL_04c5: Expected O, but got Unknown VerboseLogging = cfg.Bind<bool>("General", "VerboseLogging", false, "Write per-event diagnostics to the BepInEx log — most usefully the exact offset between you and a door's kill zone, which is what DoorwayThickness/Width/Height are tuned against. Off by default: the log is written to disk synchronously, so on a busy level it costs frames for output nobody reads."); Enabled = cfg.Bind<bool>("Doors", "Enabled", true, "Master switch for the deadly-doors mechanic."); KillPlayers = cfg.Bind<bool>("Doors", "KillPlayers", true, "Kill players caught in a closing door's threshold."); KillMonsters = cfg.Bind<bool>("Doors", "KillMonsters", false, "Kill monsters caught in a closing door's threshold (only enemies that can enter the ship)."); AffectedDoors = cfg.Bind<AffectedDoors>("Doors", "AffectedDoors", LethalDoors.AffectedDoors.Both, "Which doors are deadly: ShipDoor, TerminalDoors or Both."); SafeZoneSeconds = cfg.Bind<float>("Doors", "SafeZoneSeconds", 5f, new ConfigDescription("Grace period after the ship lands during which doors do NOT kill. 0 disables the grace period.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>())); DamageMode = cfg.Bind<DamageMode>("Doors", "DamageMode", LethalDoors.DamageMode.InstantKill, "InstantKill = kill immediately when the door finishes closing. DamageOverTime = damage players while the door stays shut on them."); DamageAmount = cfg.Bind<int>("Doors", "DamageAmount", 100, new ConfigDescription("Damage-per-second applied to players in DamageOverTime mode (ignored for InstantKill).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); ShipDoorCloseSeconds = cfg.Bind<float>("Doors.Advanced", "ShipDoorCloseSeconds", 1.6f, "Approximate duration of the ship-door close animation. The kill check fires this long after the door starts closing."); TerminalDoorCloseSeconds = cfg.Bind<float>("Doors.Advanced", "TerminalDoorCloseSeconds", 0.4f, "Delay (sec) between a facility door starting to close and the kill check firing. Big doors slam shut fast, so keep this small — larger values make the crush feel late and let quick open/close spam dodge it."); DoorwayThickness = cfg.Bind<float>("Doors.Advanced", "DoorwayThickness", 2f, new ConfigDescription("Depth (metres) of the kill slab THROUGH the doorway — the 'thin line'. A doorway is ~1.3m deep (you stand just inside/outside a closing door), so ~2.0 reliably catches someone in the opening while staying far tighter than a proximity sphere.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 6f), Array.Empty<object>())); DoorwayWidth = cfg.Bind<float>("Doors.Advanced", "DoorwayWidth", 3f, new ConfigDescription("Width (metres) of the kill slab across the doorway opening.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), Array.Empty<object>())); DoorwayHeight = cfg.Bind<float>("Doors.Advanced", "DoorwayHeight", 5f, new ConfigDescription("Height (metres) of the kill slab. Kept generous so it never misses vertically.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 12f), Array.Empty<object>())); PlayerDeathAnimationId = cfg.Bind<int>("Doors.Advanced", "PlayerDeathAnimationId", 7, "Index into StartOfRound.playerRagdolls picking which corpse spawns. 7 = the Barber (ClaySurgeonAI) 'Snipping' death — body is launched up and ragdolls (default). 0 = plain physics ragdoll that just falls. -1 = no body."); ExcludedEnemies = cfg.Bind<string>("Doors.Advanced", "ExcludedEnemies", "Girl,RadMech,Manticoil,Docile,Roaming Locust,Red Locust Bees,Locust,Earth Leviathan,Forest Giant", "Comma-separated list of enemy internal names that can NEVER be crushed (matched case-insensitively as substrings). Defaults exclude enemies that cannot physically enter the ship (Ghost Girl, Old Bird, Manticoil, Locusts, etc.)."); EnableRemoteControl = cfg.Bind<bool>("RemoteControl", "EnableRemoteControl", true, "Replace terminal 'disable mine/turret' with detonate/rampage. False = vanilla behaviour (disable)."); MineErrorChance = cfg.Bind<float>("RemoteControl", "MineErrorChance", 0.15f, new ConfigDescription("Chance (0..1) that a remote MINE command critically malfunctions (chain detonation).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); TurretErrorChance = cfg.Bind<float>("RemoteControl", "TurretErrorChance", 0.15f, new ConfigDescription("Chance (0..1) that a remote TURRET command critically malfunctions (sustained rampage). NOTE: with TurretAlwaysBerserk = true the turret goes berserk on EVERY command by design and this chance only decides whether the rampage is extended. Set TurretAlwaysBerserk = false to make this chance decide berserk-vs-disable outright.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); TurretAlwaysBerserk = cfg.Bind<bool>("RemoteControl", "TurretAlwaysBerserk", false, "false (default) = a turret code disables the turret as in vanilla, and only a TurretErrorChance roll sends it berserk — so the configured chance is what you actually see. true = every turret code sends it berserk and the chance only adds an extended rampage."); MineAlwaysDetonate = cfg.Bind<bool>("RemoteControl", "MineAlwaysDetonate", false, "false (default) = a mine code disarms the mine as in vanilla, and only a MineErrorChance roll detonates it. true = every mine code detonates it and the chance only adds the chain reaction."); MineErrorDamageMultiplier = cfg.Bind<float>("RemoteControl", "MineErrorDamageMultiplier", 2f, new ConfigDescription("On a mine error the blast is amplified: nearby mines within MineErrorChainRadius * this factor also detonate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); TurretRampageDuration = cfg.Bind<float>("RemoteControl", "TurretRampageDuration", 5f, new ConfigDescription("Extra seconds of rampage on a turret error, ON TOP of the ~10s vanilla berserk.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); TurretFlipOnBerserkExit = cfg.Bind<bool>("RemoteControl", "TurretFlipOnBerserkExit", true, "When a turret leaves berserk, turn its head 180° from the angle it had before berserk and keep it there as the new resting facing until the next berserk (it can still turn to fire at detected players)."); TurretFlipSmoothDuration = cfg.Bind<float>("RemoteControl", "TurretFlipSmoothDuration", 0.5f, new ConfigDescription("Seconds the 180° head turn takes on berserk exit. 0 = instant snap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); EnableAlliedTraps = cfg.Bind<bool>("AlliedTraps", "EnableAlliedTraps", true, "Allow a terminal trap command to HIJACK the trap instead: it stops reacting to players and starts fighting monsters for you."); AlliedChance = cfg.Bind<float>("AlliedTraps", "AlliedChance", 0.25f, new ConfigDescription("Chance (0..1) that a trap is hijackable. Rolled deterministically per trap from the level seed, so every player gets the same answer and re-entering the code cannot re-roll it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); AlliedDuration = cfg.Bind<float>("AlliedTraps", "AlliedDuration", 0f, new ConfigDescription("Seconds a hijacked trap stays on your side. 0 (default) = permanent — once a trap is hijacked it stays yours for the rest of the round.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>())); AlliedTurretRange = cfg.Bind<float>("AlliedTraps", "AlliedTurretRange", 30f, new ConfigDescription("How far (metres) an allied turret looks for monsters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 80f), Array.Empty<object>())); AlliedMineTriggerRadius = cfg.Bind<float>("AlliedTraps", "AlliedMineTriggerRadius", 2.5f, new ConfigDescription("How close (metres) a monster must get before an allied mine goes off. The blast itself is still a real explosion, so do not stand next to it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); AlliedHackSound = cfg.Bind<bool>("AlliedTraps", "AlliedHackSound", true, "Play an audible confirmation when a trap is successfully hijacked (it powers down, then comes back online on your side)."); AlliedTint = cfg.Bind<bool>("AlliedTraps", "AlliedTint", true, "Recolour a hijacked trap green (turret laser, mine indicator) so you can tell it is on your side."); MiniTurretAlwaysAllied = cfg.Bind<bool>("AlliedTraps", "MiniTurretAlwaysAllied", true, "DefendFacility's mini turret is allied the moment it exists — the crew bought and carried it in, so it never has to be hacked and never turns on you. Its friendliness does not expire. Ignored when DefendFacility is not installed."); MineErrorChainRadius = cfg.Bind<float>("RemoteControl.Advanced", "MineErrorChainRadius", 8f, "Base radius (metres) used to find neighbouring mines for the chain reaction on a mine error."); MineWarnBeforeDetonate = cfg.Bind<bool>("RemoteControl.Advanced", "MineWarnBeforeDetonate", false, "If true, a NORMAL (non-error) mine command plays the warning beep before exploding. Errors always detonate instantly."); } } [BepInPlugin("Solon.TerminalThatCanKill", "Terminal that can kill", "1.5.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "Solon.TerminalThatCanKill"; public const string PluginName = "Terminal that can kill"; public const string PluginVersion = "1.5.0"; internal const string FairAiGuid = "GoldenKitten.FairAI"; internal const string DefendFacilityGuid = "com.yourname.defendfacility"; private readonly Harmony _harmony = new Harmony("Solon.TerminalThatCanKill"); public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } internal static ModConfig Config { get; private set; } internal static bool Verbose { get { if (Config != null) { return Config.VerboseLogging.Value; } return false; } } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Config = new ModConfig(((BaseUnityPlugin)this).Config); try { _harmony.PatchAll(typeof(Plugin).Assembly); FairAiCompat.TryPatch(_harmony); MiniTurretCompat.Init(); Log.LogInfo((object)"Terminal that can kill v1.5.0 loaded — Harmony patches applied."); } catch (Exception arg) { Log.LogError((object)$"Failed to apply Harmony patches: {arg}"); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Solon.TerminalThatCanKill"; public const string PLUGIN_NAME = "Terminal that can kill"; public const string PLUGIN_VERSION = "1.5.0"; } } namespace LethalDoors.Traps { internal static class AlliedTraps { private sealed class TurretState { public float Until; public float NextHit; public float NextScan; public EnemyAI Target; public int HitForce; public bool Engaged; public Transform AimHelper; } private sealed class MineState { public float Until; public float NextScan; } private static readonly Dictionary<Turret, TurretState> _turrets = new Dictionary<Turret, TurretState>(); private static readonly Dictionary<Landmine, MineState> _mines = new Dictionary<Landmine, MineState>(); private static readonly List<Turret> _scratchTurrets = new List<Turret>(); private static readonly List<Landmine> _scratchMines = new List<Landmine>(); private const float ScanInterval = 0.25f; private const float VanillaFireInterval = 0.21f; public static void Clear() { foreach (KeyValuePair<Turret, TurretState> turret in _turrets) { DestroyHelper(turret.Value); } _turrets.Clear(); _mines.Clear(); TrapVisuals.Clear(); } public static bool RollAllied(NetworkBehaviour trap) { if ((Object)(object)trap == (Object)null) { return false; } if (!Plugin.Config.EnableAlliedTraps.Value) { return false; } float value = Plugin.Config.AlliedChance.Value; if (value <= 0f) { return false; } if (value >= 1f) { return true; } float value2 = Random.value; bool flag = value2 < value; Plugin.Log.LogInfo((object)string.Format("Hijack roll {0:F3} vs {1:F3} -> {2}.", value2, value, flag ? "HIJACK" : "no")); return flag; } private static float ExpiryFromConfig() { float value = Plugin.Config.AlliedDuration.Value; if (!(value <= 0f)) { return Time.time + value; } return float.MaxValue; } public static void MakeAllied(Turret turret) { if (!((Object)(object)turret == (Object)null)) { if (!_turrets.TryGetValue(turret, out var value)) { value = new TurretState(); _turrets[turret] = value; } value.Until = ExpiryFromConfig(); TrapVisuals.ApplyAllied((Component)(object)turret); } } public static void MakeAlliedPermanently(Turret turret) { if (!((Object)(object)turret == (Object)null)) { if (!_turrets.TryGetValue(turret, out var value)) { value = new TurretState(); _turrets[turret] = value; } value.Until = float.MaxValue; } } public static void MakeAllied(Landmine mine) { if (!((Object)(object)mine == (Object)null)) { if (!_mines.TryGetValue(mine, out var value)) { value = new MineState(); _mines[mine] = value; } value.Until = ExpiryFromConfig(); TrapVisuals.ApplyAllied((Component)(object)mine); } } public static bool TryGetAimTarget(Turret turret, out Transform aim) { aim = null; if ((Object)(object)turret == (Object)null || _turrets.Count == 0) { return false; } if (!_turrets.TryGetValue(turret, out var value)) { return false; } if (!value.Engaged || (Object)(object)value.AimHelper == (Object)null || Time.time >= value.Until) { return false; } aim = value.AimHelper; return true; } private static void DestroyHelper(TurretState s) { if (s != null && (Object)(object)s.AimHelper != (Object)null) { Object.Destroy((Object)(object)((Component)s.AimHelper).gameObject); } if (s != null) { s.AimHelper = null; } } public static bool IsAllied(Turret turret) { if ((Object)(object)turret == (Object)null || _turrets.Count == 0) { return false; } if (_turrets.TryGetValue(turret, out var value)) { return Time.time < value.Until; } return false; } public static bool IsAllied(Landmine mine) { if ((Object)(object)mine == (Object)null || _mines.Count == 0) { return false; } if (_mines.TryGetValue(mine, out var value)) { return Time.time < value.Until; } return false; } public static void Tick() { if (_turrets.Count > 0) { TickTurrets(); } if (_mines.Count > 0) { TickMines(); } } private static void TickTurrets() { //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; bool active = FairAiCompat.Active; _scratchTurrets.Clear(); foreach (KeyValuePair<Turret, TurretState> turret in _turrets) { _scratchTurrets.Add(turret.Key); } for (int i = 0; i < _scratchTurrets.Count; i++) { Turret val = _scratchTurrets[i]; if ((Object)(object)val == (Object)null) { _turrets.Remove(val); continue; } TurretState turretState = _turrets[val]; if ((Object)(object)turretState.Target != (Object)null && GameCompat.IsEnemyDead(turretState.Target)) { turretState.Target = null; } if (time >= turretState.Until) { Plugin.Log.LogInfo((object)"Allied turret: hijack expired, back to normal."); TrapVisuals.Restore((Component)(object)val); GameCompat.DisengageTurret(val); DestroyHelper(turretState); _turrets.Remove(val); } else { if (active) { continue; } if (time >= turretState.NextScan) { turretState.NextScan = time + 0.25f; EnemyAI val2 = FindEnemyForTurret(val); if ((Object)(object)val2 != (Object)(object)turretState.Target) { turretState.Target = val2; turretState.HitForce = ((!((Object)(object)val2 != (Object)null)) ? 1 : Mathf.Max(1, Mathf.CeilToInt((float)val2.enemyHP / 2f))); } } if (!((Object)(object)turretState.Target != (Object)null) || GameCompat.IsEnemyDead(turretState.Target)) { if (turretState.Engaged) { turretState.Engaged = false; GameCompat.DisengageTurret(val); } continue; } if ((Object)(object)turretState.AimHelper == (Object)null) { turretState.AimHelper = new GameObject("TTCK_AlliedAimTarget").transform; } turretState.AimHelper.position = ((Component)turretState.Target).transform.position + Vector3.up * 0.7f; turretState.Engaged = true; GameCompat.EngageTurret(val); if (GameCompat.IsHost && GameCompat.IsFiring(val) && time >= turretState.NextHit) { turretState.NextHit = time + 0.21f; GameCompat.HurtEnemy(turretState.Target, turretState.HitForce); } } } } private static void TickMines() { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) if (!GameCompat.IsHost) { return; } float time = Time.time; float value = Plugin.Config.AlliedMineTriggerRadius.Value; float num = value * value; _scratchMines.Clear(); foreach (KeyValuePair<Landmine, MineState> mine in _mines) { _scratchMines.Add(mine.Key); } for (int i = 0; i < _scratchMines.Count; i++) { Landmine val = _scratchMines[i]; if ((Object)(object)val == (Object)null) { _mines.Remove(val); continue; } MineState mineState = _mines[val]; if (time >= mineState.Until) { TrapVisuals.Restore((Component)(object)val); _mines.Remove(val); } else { if (time < mineState.NextScan) { continue; } mineState.NextScan = time + 0.25f; List<EnemyAI> list = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.SpawnedEnemies : null); if (list == null) { continue; } Vector3 position = ((Component)val).transform.position; for (int j = 0; j < list.Count; j++) { EnemyAI val2 = list[j]; if (!((Object)(object)val2 == (Object)null) && !GameCompat.IsEnemyDead(val2)) { Vector3 val3 = ((Component)val2).transform.position - position; if (!(((Vector3)(ref val3)).sqrMagnitude > num)) { Plugin.Log.LogInfo((object)("Allied mine detonating on '" + GameCompat.EnemyName(val2) + "'.")); TrapVisuals.Restore((Component)(object)val); GameCompat.DetonateMine(val); _mines.Remove(val); break; } } } } } } private static EnemyAI FindEnemyForTurret(Turret turret) { //IL_008b: 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_009a: 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) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: 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) List<EnemyAI> list = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.SpawnedEnemies : null); if (list == null || list.Count == 0) { return null; } Transform val = (((Object)(object)turret.centerPoint != (Object)null) ? turret.centerPoint : ((Component)turret).transform); float value = Plugin.Config.AlliedTurretRange.Value; float num = value * value; EnemyAI result = null; for (int i = 0; i < list.Count; i++) { EnemyAI val2 = list[i]; if (!((Object)(object)val2 == (Object)null) && !GameCompat.IsEnemyDead(val2)) { Vector3 val3 = ((Component)val2).transform.position + Vector3.up * 0.7f; Vector3 val4 = val3 - val.position; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (!(sqrMagnitude > num) && (!((Object)(object)turret.forwardFacingPos != (Object)null) || !(Vector3.Angle(val3 - val.position, turret.forwardFacingPos.forward) > turret.rotationRange)) && GameCompat.HasLineOfSight(val.position, val3)) { num = sqrMagnitude; result = val2; } } } return result; } } internal static class RemoteControlManager { private const float VanillaBerserkSeconds = 10.3f; private const float RampageTickSeconds = 0.5f; public static void HandleMine(Landmine mine) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mine == (Object)null) { return; } if (AlliedTraps.IsAllied(mine)) { Plugin.Log.LogInfo((object)"Remote mine is already hijacked — leaving it on our side."); return; } if (AlliedTraps.RollAllied((NetworkBehaviour)(object)mine)) { GameCompat.BroadcastHijack(mine); AlliedTraps.MakeAllied(mine); Plugin.Log.LogInfo((object)"Remote mine HIJACKED — it now ignores players and waits for monsters."); return; } float value = Plugin.Config.MineErrorChance.Value; float value2 = Random.value; bool flag = value2 < value; if (!flag && !Plugin.Config.MineAlwaysDetonate.Value) { Plugin.Log.LogInfo((object)$"Remote mine: roll {value2:F3} >= {value:F3} -> vanilla disable."); GameCompat.DisableMineQuiet(mine); return; } if (!flag && Plugin.Config.MineWarnBeforeDetonate.Value) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(WarnThenDetonate(mine)); Plugin.Log.LogInfo((object)$"Remote mine: armed, warning beep (roll {value2:F3} >= {value:F3})."); return; } GameCompat.DetonateMine(mine); if (flag) { Plugin.Log.LogWarning((object)$"Remote mine CRITICAL ERROR (roll {value2:F3} < {value:F3}) — chain detonation!"); ChainDetonateNearby(((Component)mine).transform.position); } else { Plugin.Log.LogInfo((object)$"Remote mine: detonated (roll {value2:F3} >= {value:F3}, no error)."); } } private static IEnumerator WarnThenDetonate(Landmine mine) { yield return (object)new WaitForSeconds(0.85f); GameCompat.DetonateMine(mine); } private static void ChainDetonateNearby(Vector3 origin) { //IL_003f: 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_0045: 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) float num = Plugin.Config.MineErrorChainRadius.Value * Plugin.Config.MineErrorDamageMultiplier.Value; float num2 = num * num; Landmine[] array = Object.FindObjectsOfType<Landmine>(); foreach (Landmine val in array) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - origin; if (!(((Vector3)(ref val2)).sqrMagnitude > num2)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(DelayedDetonate(val, Random.Range(0.02f, 0.25f))); } } } } private static IEnumerator DelayedDetonate(Landmine mine, float delay) { yield return (object)new WaitForSeconds(delay); GameCompat.DetonateMine(mine); } public static bool HandleTurret(Turret turret) { if ((Object)(object)turret == (Object)null) { return false; } if (AlliedTraps.IsAllied(turret)) { if (MiniTurretCompat.IsMiniTurret(turret)) { return false; } Plugin.Log.LogInfo((object)"Turret is already hijacked — leaving it on our side."); return true; } if (AlliedTraps.RollAllied((NetworkBehaviour)(object)turret)) { GameCompat.BroadcastHijack(turret); AlliedTraps.MakeAllied(turret); Plugin.Log.LogInfo((object)"Remote turret HIJACKED — it now ignores players and hunts monsters."); return true; } float value = Plugin.Config.TurretErrorChance.Value; float value2 = Random.value; bool flag = value2 < value; if (!flag && !Plugin.Config.TurretAlwaysBerserk.Value) { Plugin.Log.LogInfo((object)$"Remote turret: roll {value2:F3} >= {value:F3} -> vanilla disable."); return false; } GameCompat.BerserkTurret(turret); if (flag) { float value3 = Plugin.Config.TurretRampageDuration.Value; Plugin.Log.LogWarning((object)$"Remote turret CRITICAL ERROR (roll {value2:F3} < {value:F3}) — rampage extended by {value3:0.#}s."); ((MonoBehaviour)Plugin.Instance).StartCoroutine(Rampage(turret, value3)); } else { Plugin.Log.LogInfo((object)$"Remote turret: berserk (roll {value2:F3} >= {value:F3}, no error)."); } return true; } private static IEnumerator Rampage(Turret turret, float extraSeconds) { float end = Time.time + 10.3f + extraSeconds; WaitForSeconds wait = new WaitForSeconds(0.5f); while (Time.time < end && !((Object)(object)turret == (Object)null)) { if (!GameCompat.IsBerserk(turret)) { GameCompat.BerserkTurret(turret); } else { GameCompat.SustainBerserk(turret, 1.5f); } yield return wait; } } } internal static class TrapVisuals { private sealed class Record { public readonly List<Light> Lights = new List<Light>(); public readonly List<Color> LightColors = new List<Color>(); public readonly List<LineRenderer> Lines = new List<LineRenderer>(); public readonly List<Color> LineStart = new List<Color>(); public readonly List<Color> LineEnd = new List<Color>(); public readonly List<Renderer> Renderers = new List<Renderer>(); public readonly List<int> Props = new List<int>(); public readonly List<Color> Colors = new List<Color>(); } private static readonly string[] ColorProps = new string[5] { "_EmissiveColor", "_EmissionColor", "_BaseColor", "_Color", "_UnlitColor" }; private static readonly int[] ColorPropIds = BuildPropIds(); private static readonly MaterialPropertyBlock _block = new MaterialPropertyBlock(); private static readonly List<Material> _matBuffer = new List<Material>(); private static readonly Dictionary<Component, Record> _records = new Dictionary<Component, Record>(); private static int[] BuildPropIds() { int[] array = new int[ColorProps.Length]; for (int i = 0; i < ColorProps.Length; i++) { array[i] = Shader.PropertyToID(ColorProps[i]); } return array; } public static void Clear() { _records.Clear(); } public static void ApplyAllied(Component trap) { //IL_005c: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: 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) if ((Object)(object)trap == (Object)null || !Plugin.Config.AlliedTint.Value || _records.ContainsKey(trap)) { return; } Record record = new Record(); try { Light[] componentsInChildren = trap.GetComponentsInChildren<Light>(true); foreach (Light val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { record.Lights.Add(val); record.LightColors.Add(val.color); val.color = ToGreen(val.color); } } LineRenderer[] componentsInChildren2 = trap.GetComponentsInChildren<LineRenderer>(true); foreach (LineRenderer val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { record.Lines.Add(val2); record.LineStart.Add(val2.startColor); record.LineEnd.Add(val2.endColor); val2.startColor = ToGreen(val2.startColor); val2.endColor = ToGreen(val2.endColor); TintRenderer((Renderer)(object)val2, record, requireRed: false); } } Renderer[] componentsInChildren3 = trap.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren3) { if (!((Object)(object)val3 == (Object)null) && !(val3 is LineRenderer)) { TintRenderer(val3, record, requireRed: true); } } } catch { } _records[trap] = record; Plugin.Log.LogInfo((object)($"Allied tint on '{((Object)trap.gameObject).name}': {record.Lights.Count} light(s), " + $"{record.Lines.Count} beam(s), {record.Renderers.Count} indicator material(s).")); } public static void Restore(Component trap) { //IL_0051: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)trap == (Object)null || !_records.TryGetValue(trap, out var value)) { return; } _records.Remove(trap); try { for (int i = 0; i < value.Lights.Count; i++) { if ((Object)(object)value.Lights[i] != (Object)null) { value.Lights[i].color = value.LightColors[i]; } } for (int j = 0; j < value.Lines.Count; j++) { if (!((Object)(object)value.Lines[j] == (Object)null)) { value.Lines[j].startColor = value.LineStart[j]; value.Lines[j].endColor = value.LineEnd[j]; } } for (int k = 0; k < value.Renderers.Count; k++) { Renderer val = value.Renderers[k]; if (!((Object)(object)val == (Object)null)) { val.GetPropertyBlock(_block); _block.SetColor(value.Props[k], value.Colors[k]); val.SetPropertyBlock(_block); } } } catch { } } private static void TintRenderer(Renderer r, Record rec, bool requireRed) { //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_0068: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) r.GetSharedMaterials(_matBuffer); if (_matBuffer.Count == 0) { return; } r.GetPropertyBlock(_block); bool flag = false; for (int i = 0; i < _matBuffer.Count; i++) { Material val = _matBuffer[i]; if ((Object)(object)val == (Object)null) { continue; } for (int j = 0; j < ColorPropIds.Length; j++) { int num = ColorPropIds[j]; if (val.HasProperty(num)) { Color color = val.GetColor(num); if ((!requireRed || IsDominantlyRed(color)) && !(((Color)(ref color)).maxColorComponent <= 0.001f)) { rec.Renderers.Add(r); rec.Props.Add(num); rec.Colors.Add(color); _block.SetColor(num, ToGreen(color)); flag = true; } } } } _matBuffer.Clear(); if (flag) { r.SetPropertyBlock(_block); } } private static bool IsDominantlyRed(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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) if (c.r > 0.25f && c.r > c.g * 1.8f) { return c.r > c.b * 1.8f; } return false; } private static Color ToGreen(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_003c: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(c.r, Mathf.Max(c.g, c.b)); if (num <= 0f) { num = 1f; } return new Color(0f, num, 0f, c.a); } } } namespace LethalDoors.Patches { [HarmonyPatch(typeof(Turret), "CheckForPlayersInLineOfSight")] internal static class Turret_IgnorePlayers_Patch { [HarmonyPostfix] private static void Postfix(Turret __instance, ref PlayerControllerB __result) { if (!((Object)(object)__result == (Object)null) && AlliedTraps.IsAllied(__instance)) { __result = null; } } } [HarmonyPatch(typeof(Turret), "SetTargetToPlayerBody")] internal static class Turret_AimAtMonster_Patch { [HarmonyPrefix] private static bool Prefix(Turret __instance) { if (!AlliedTraps.IsAllied(__instance)) { return true; } if (FairAiCompat.Active) { return true; } if (AlliedTraps.TryGetAimTarget(__instance, out var aim)) { __instance.targetingDeadPlayer = false; __instance.targetTransform = aim; return false; } __instance.targetPlayerWithRotation = null; return false; } } [HarmonyPatch(typeof(Landmine), "OnTriggerEnter")] internal static class Landmine_IgnorePlayers_Patch { [HarmonyPrefix] private static bool Prefix(Landmine __instance) { return !AlliedTraps.IsAllied(__instance); } } [HarmonyPatch(typeof(Turret), "EnterBerserkModeClientRpc")] internal static class Turret_AlliedSync_Patch { [HarmonyPostfix] private static void Postfix(Turret __instance, int playerWhoTriggered) { if (playerWhoTriggered == 424242) { bool num = AlliedTraps.IsAllied(__instance); AlliedTraps.MakeAllied(__instance); GameCompat.CancelBerserk(__instance); if (!num) { GameCompat.PlayHackCue(__instance); } } } } [HarmonyPatch(typeof(Landmine), "ToggleMineEnabledLocalClient")] internal static class Landmine_AlliedSync_Patch { private const float BlinkWindow = 0.2f; private static readonly Dictionary<Landmine, float> _offAt = new Dictionary<Landmine, float>(); public static void Clear() { _offAt.Clear(); } [HarmonyPostfix] private static void Postfix(Landmine __instance, bool enabled) { if ((Object)(object)__instance == (Object)null) { return; } float value; if (!enabled) { _offAt[__instance] = Time.time; } else if (_offAt.TryGetValue(__instance, out value)) { _offAt.Remove(__instance); if (!(Time.time - value > 0.2f) && !AlliedTraps.IsAllied(__instance)) { AlliedTraps.MakeAllied(__instance); GameCompat.PlayHackCue(__instance); } } } } internal static class FairAiCompat { private const string PluginGuid = "GoldenKitten.FairAI"; private static PropertyInfo _isPlayerProp; public static bool Active { get; private set; } public static void TryPatch(Harmony harmony) { try { if (Chainloader.PluginInfos.ContainsKey("GoldenKitten.FairAI")) { Type type = FindType("TurretAIPatch"); if (type == null) { Plugin.Log.LogWarning((object)"Fair AI is loaded but its turret patch class was not found — hijacked turrets may still target players."); return; } bool num = Hook(harmony, type, "ApplyDamageToLocalPlayer", "BlockAlliedPlayerDamage"); bool flag = Hook(harmony, type, "SweepForPlayer", "BlockAlliedPlayerSweep"); Hook(harmony, type, "SetCurrentTarget", "BlockAlliedPlayerTarget"); Active = num && flag; Plugin.Log.LogInfo((object)(Active ? "Fair AI detected — it drives the turrets, and hijacked ones are carved out of its player targeting and damage." : "Fair AI detected but its turret hooks did not match; falling back to our own allied turret handling.")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fair AI compatibility patch failed: " + ex.Message)); } } private static bool Hook(Harmony harmony, Type patchType, string method, string prefix) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(patchType, method, (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)("Fair AI: " + method + " not found, that hook is skipped.")); return false; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(FairAiCompat), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fair AI: could not patch " + method + " — " + ex.Message)); return false; } } private static bool BlockAlliedPlayerDamage(Turret turret) { return !AlliedTraps.IsAllied(turret); } private static bool BlockAlliedPlayerSweep(Turret turret, ref PlayerControllerB __result) { if (!AlliedTraps.IsAllied(turret)) { return true; } __result = null; return false; } private static bool BlockAlliedPlayerTarget(Turret turret, object[] __args) { try { if (!AlliedTraps.IsAllied(turret)) { return true; } if (__args == null || __args.Length < 3) { return true; } object obj = __args[2]; if (obj == null) { return true; } if (_isPlayerProp == null || _isPlayerProp.DeclaringType != obj.GetType()) { _isPlayerProp = obj.GetType().GetProperty("IsPlayer"); } bool flag = default(bool); int num; if (_isPlayerProp != null) { object value = _isPlayerProp.GetValue(obj); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return false; } } catch { } return true; } private static Type FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name != "FairAI") { continue; } try { Type type = assembly.GetType(name, throwOnError: false); if (type != null) { return type; } Type[] types = assembly.GetTypes(); foreach (Type type2 in types) { if (type2.Name == name) { return type2; } } } catch { } } return null; } } [HarmonyPatch(typeof(HangarShipDoor), "Update")] internal static class HangarShipDoorPatch { private const string ClosedStateName = "ShipDoorClose"; private static readonly Vector3 ShipDoorZoneCenter = new Vector3(-5.72f, 0.305f, -14.1f); private static float _closeTimer = -1f; private static bool _crushed; private static float _dotAccumulator; private static bool _fullyClosed; private static bool _zoneValid; private static DoorZone _zone; [HarmonyPostfix] private static void Postfix(HangarShipDoor __instance) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Config.Enabled.Value || Plugin.Config.AffectedDoors.Value == AffectedDoors.TerminalDoors) { return; } if (!((Object)(object)StartOfRound.Instance != (Object)null) || !StartOfRound.Instance.shipHasLanded || !(__instance.doorPower < 1f)) { _closeTimer = -1f; _crushed = false; _dotAccumulator = 0f; _fullyClosed = false; _zoneValid = false; return; } _closeTimer = ((_closeTimer < 0f) ? 0f : (_closeTimer + Time.deltaTime)); if (!_fullyClosed) { bool flag = false; Animator shipDoorsAnimator = __instance.shipDoorsAnimator; if ((Object)(object)shipDoorsAnimator != (Object)null) { AnimatorStateInfo currentAnimatorStateInfo = shipDoorsAnimator.GetCurrentAnimatorStateInfo(0); flag = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("ShipDoorClose"); } _fullyClosed = flag || _closeTimer >= Plugin.Config.ShipDoorCloseSeconds.Value; if (!_fullyClosed) { return; } } bool flag2 = Plugin.Config.DamageMode.Value == DamageMode.DamageOverTime; if (flag2 || !_crushed) { if (!_zoneValid) { _zone = BuildShipZone(__instance); _zoneValid = true; } if (flag2) { DoorCrushManager.ExecuteDamageTick(DoorKind.Ship, _zone, ref _dotAccumulator); return; } _crushed = true; DoorCrushManager.ExecuteCrush(DoorKind.Ship, _zone); } } private static DoorZone BuildShipZone(HangarShipDoor door) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0064: 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_002b: 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_0037: 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_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_005d: 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) ModConfig config = Plugin.Config; Vector3 shipDoorZoneCenter = ShipDoorZoneCenter; Vector3 val = Vector3.forward; if ((Object)(object)door.outsideDoorPoint != (Object)null) { val = door.outsideDoorPoint.forward; Vector3 val2 = new Vector3(val.x, 0f, val.z); if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val = door.outsideDoorPoint.position - shipDoorZoneCenter; } } return DoorZone.FromThroughAxis(shipDoorZoneCenter, val, config.DoorwayWidth.Value, config.DoorwayHeight.Value, config.DoorwayThickness.Value); } } internal static class MiniTurretCompat { private const string PluginGuid = "com.yourname.defendfacility"; private const string AssemblyName = "DefendFacility"; private const string TagTypeName = "MiniTurretTag"; private static Type _tagType; public static bool Active => _tagType != null; public static void Init() { try { if (Chainloader.PluginInfos.ContainsKey("com.yourname.defendfacility")) { _tagType = FindType("DefendFacility", "MiniTurretTag"); Plugin.Log.LogInfo((object)(Active ? "DefendFacility detected — its mini turret is always allied and never needs hacking." : "DefendFacility detected but its mini turret marker was not found; mini turrets behave like ordinary turrets.")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("DefendFacility compatibility setup failed: " + ex.Message)); } } public static bool IsMiniTurret(Turret turret) { if (!Active || (Object)(object)turret == (Object)null) { return false; } try { return (Object)(object)((Component)turret).GetComponent(_tagType) != (Object)null; } catch { return false; } } public static void TryRegister(Turret turret) { if (Active && Plugin.Config.EnableAlliedTraps.Value && Plugin.Config.MiniTurretAlwaysAllied.Value && !((Object)(object)turret == (Object)null) && !AlliedTraps.IsAllied(turret) && IsMiniTurret(turret)) { AlliedTraps.MakeAlliedPermanently(turret); Plugin.Log.LogInfo((object)"Mini turret is on the crew's side — allied from the start, no code needed."); if ((Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(TintNextFrame(turret)); } } } public static void RegisterExisting() { if (!Active) { return; } try { Turret[] array = Object.FindObjectsOfType<Turret>(true); for (int i = 0; i < array.Length; i++) { TryRegister(array[i]); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Mini turret sweep failed: " + ex.Message)); } } private static IEnumerator TintNextFrame(Turret turret) { yield return null; if ((Object)(object)turret != (Object)null) { TrapVisuals.ApplyAllied((Component)(object)turret); } } private static Type FindType(string assemblyName, string typeName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name != assemblyName) { continue; } try { Type type = assembly.GetType(typeName, throwOnError: false); if (type != null) { return type; } Type[] types = assembly.GetTypes(); foreach (Type type2 in types) { if (type2.Name == typeName) { return type2; } } } catch { } } return null; } } [HarmonyPatch(typeof(Turret), "Start")] internal static class Turret_MiniTurretAllied_Patch { [HarmonyPostfix] private static void Postfix(Turret __instance) { MiniTurretCompat.TryRegister(__instance); } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void OnStart(StartOfRound __instance) { if ((Object)(object)LethalDoorsSession.Instance == (Object)null) { ((Component)__instance).gameObject.AddComponent<LethalDoorsSession>(); } TerminalDoorCrush.Clear(); TaoTrapLookup.Clear(); TurretHeadFlipPatch.ClearStates(); AlliedTraps.Clear(); MiniTurretCompat.RegisterExisting(); Landmine_AlliedSync_Patch.Clear(); } } internal static class TaoTrapLookup { private sealed class Entry { public Landmine Mine; public Turret Turret; } private static readonly Dictionary<TerminalAccessibleObject, Entry> _cache = new Dictionary<TerminalAccessibleObject, Entry>(); private static readonly List<TerminalAccessibleObject> _dead = new List<TerminalAccessibleObject>(); public static void Clear() { _cache.Clear(); _dead.Clear(); } public static void Resolve(TerminalAccessibleObject tao, out Landmine mine, out Turret turret) { mine = null; turret = null; if (!((Object)(object)tao == (Object)null)) { if (!_cache.TryGetValue(tao, out var value)) { Prune(); value = new Entry(); value.Mine = ((Component)tao).GetComponent<Landmine>() ?? ((Component)tao).GetComponentInParent<Landmine>() ?? ((Component)tao).GetComponentInChildren<Landmine>(); value.Turret = (((Object)(object)value.Mine != (Object)null) ? null : (((Component)tao).GetComponent<Turret>() ?? ((Component)tao).GetComponentInParent<Turret>() ?? ((Component)tao).GetComponentInChildren<Turret>())); _cache[tao] = value; } mine = value.Mine; turret = value.Turret; } } public static bool IsTrap(TerminalAccessibleObject tao) { Resolve(tao, out var mine, out var turret); if (!((Object)(object)mine != (Object)null)) { return (Object)(object)turret != (Object)null; } return true; } private static void Prune() { if (_cache.Count == 0) { return; } _dead.Clear(); foreach (KeyValuePair<TerminalAccessibleObject, Entry> item in _cache) { if ((Object)(object)item.Key == (Object)null) { _dead.Add(item.Key); } } for (int i = 0; i < _dead.Count; i++) { _cache.Remove(_dead[i]); } _dead.Clear(); } } [HarmonyPatch(typeof(TerminalAccessibleObject), "CallFunctionFromTerminal")] internal static class TerminalAccessibleObject_CallFunction_Patch { [HarmonyPrefix] private static bool Prefix(TerminalAccessibleObject __instance) { try { TaoTrapLookup.Resolve(__instance, out var mine, out var turret); if ((Object)(object)mine == (Object)null && (Object)(object)turret == (Object)null) { return true; } if (!Plugin.Config.EnableRemoteControl.Value) { return true; } if ((Object)(object)mine != (Object)null) { RemoteControlManager.HandleMine(mine); return false; } return !RemoteControlManager.HandleTurret(turret); } catch (Exception arg) { Plugin.Log.LogError((object)$"CallFunctionFromTerminal prefix error: {arg}"); } return true; } } [HarmonyPatch(typeof(TerminalAccessibleObject), "SetDoorOpen")] internal static class TerminalAccessibleObject_SetDoor_Patch { [HarmonyPrefix] private static void Prefix(TerminalAccessibleObject __instance, out bool __state) { __state = __instance.isDoorOpen; } [HarmonyPostfix] private static void Postfix(TerminalAccessibleObject __instance, bool open, bool __state) { if (__instance.isBigDoor && !TaoTrapLookup.IsTrap(__instance) && Plugin.Config.Enabled.Value && Plugin.Config.AffectedDoors.Value != AffectedDoors.ShipDoor && __state && !open) { TerminalDoorCrush.OnClosed(__instance); } } } [HarmonyPatch(typeof(Terminal), "CallFunctionInAccessibleTerminalObject")] internal static class TerminalReachPatch { [HarmonyPostfix] private static void Postfix(Terminal __instance, string word) { if (string.IsNullOrEmpty(word)) { return; } try { TerminalAccessibleObject[] array = Object.FindObjectsOfType<TerminalAccessibleObject>(true); if (array == null || array.Length == 0) { return; } bool flag = false; foreach (TerminalAccessibleObject val in array) { if ((Object)(object)val != (Object)null && !((Behaviour)val).isActiveAndEnabled && val.objectCode == word) { flag = true; break; } } if (!flag) { return; } HashSet<TerminalAccessibleObject> hashSet = new HashSet<TerminalAccessibleObject>(Object.FindObjectsOfType<TerminalAccessibleObject>()); foreach (TerminalAccessibleObject val2 in array) { if (!((Object)(object)val2 == (Object)null) && !(val2.objectCode != word) && !hashSet.Contains(val2)) { Plugin.Log.LogInfo((object)("Terminal code '" + word + "' reached a hidden trap object on '" + ((Object)((Component)val2).gameObject).name + "' (vanilla could not see it).")); __instance.broadcastedCodeThisFrame = true; val2.CallFunctionFromTerminal(); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Terminal reach patch failed: {arg}"); } } } [HarmonyPatch(typeof(Turret), "Update")] internal static class TurretHeadFlipPatch { private sealed class State { public Transform Head; public int PrevMode = -1; public float RememberedYaw; public bool HasResting; public float RestingYaw; public bool Animating; public float AnimTime; public float AnimDuration; public float AnimFromYaw; public float AnimToYaw; } private const int Berserk = 3; private const int Detection = 0; private static readonly Dictionary<Turret, State> _states = new Dictionary<Turret, State>(); private static readonly List<Turret> _dead = new List<Turret>(); public static void ClearStates() { _states.Clear(); } [HarmonyPostfix] private static void Postfix(Turret __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected I4, but got Unknown int num = (int)__instance.turretMode; if ((num != 3 && _states.Count == 0) || !Plugin.Config.TurretFlipOnBerserkExit.Value || AlliedTraps.IsAllied(__instance)) { return; } try { Process(__instance, num); } catch (Exception arg) { Plugin.Log.LogError((object)$"Turret head flip error: {arg}"); } } private static void Process(Turret turret, int mode) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (!_states.TryGetValue(turret, out var value)) { if (mode != 3) { return; } Prune(); value = new State { Head = FindHead(turret), PrevMode = 0 }; _states[turret] = value; } if ((Object)(object)value.Head == (Object)null) { _states.Remove(turret); return; } if (value.PrevMode != 3 && mode == 3) { value.RememberedYaw = value.Head.localEulerAngles.y; value.Animating = false; } else if (value.PrevMode == 3 && mode != 3) { value.RestingYaw = Normalize(value.RememberedYaw + 180f); value.HasResting = true; float value2 = Plugin.Config.TurretFlipSmoothDuration.Value; if (value2 > 0.01f) { value.Animating = true; value.AnimTime = 0f; value.AnimDuration = value2; value.AnimFromYaw = value.Head.localEulerAngles.y; value.AnimToYaw = value.RestingYaw; } else { value.Animating = false; SetYaw(value.Head, value.RestingYaw); } } value.PrevMode = mode; if (mode == 3) { return; } if (value.Animating) { value.AnimTime += Time.deltaTime; float num = Mathf.Clamp01(value.AnimTime / value.AnimDuration); SetYaw(value.Head, Mathf.LerpAngle(value.AnimFromYaw, value.AnimToYaw, num)); if (num >= 1f) { value.Animating = false; } } else if (value.HasResting && mode == 0) { SetYaw(value.Head, value.RestingYaw); } } private static void SetYaw(Transform head, float yaw) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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) Vector3 localEulerAngles = head.localEulerAngles; if (!(Mathf.Abs(Mathf.DeltaAngle(localEulerAngles.y, yaw)) < 0.01f)) { head.localEulerAngles = new Vector3(localEulerAngles.x, yaw, localEulerAngles.z); } } private static void Prune() { if (_states.Count == 0) { return; } _dead.Clear(); foreach (KeyValuePair<Turret, State> state in _states) { if ((Object)(object)state.Key == (Object)null) { _dead.Add(state.Key); } } for (int i = 0; i < _dead.Count; i++) { _states.Remove(_dead[i]); } _dead.Clear(); } private static float Normalize(float angle) { return Mathf.Repeat(angle, 360f); } private static Transform FindHead(Turret turret) { if ((Object)(object)turret.turretRod != (Object)null) { return turret.turretRod; } Transform root = (((Object)(object)((Component)turret).transform.root != (Object)null) ? ((Component)turret).transform.root : ((Component)turret).transform); Transform val = DeepFind(root, "TurretHead") ?? DeepFind(root, "Gun"); if ((Object)(object)val != (Object)null) { return val; } Transform parent = ((Component)turret).transform.parent; if ((Object)(object)parent != (Object)null && parent.childCount > 1) { return parent.GetChild(1); } return ((Component)turret).transform; } private static Transform DeepFind(Transform root, string name) { if ((Object)(object)root == (Object)null) { return null; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name == name) { return val; } } return null; } } } namespace LethalDoors.Doors { public enum DoorKind { Ship, Terminal } internal static class DoorCrushManager { public static void ExecuteCrush(DoorKind kind, DoorZone zone) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) ModConfig config = Plugin.Config; if (!config.Enabled.Value || !AppliesTo(kind) || ((Object)(object)LethalDoorsSession.Instance != (Object)null && LethalDoorsSession.Instance.InSafePeriod)) { return; } if (config.KillPlayers.Value) { PlayerControllerB localPlayer = GameCompat.LocalPlayer; if (GameCompat.IsPlayerAlive(localPlayer)) { if (zone.Contains(((Component)localPlayer).transform.position)) { GameCompat.KillLocalPlayer(localPlayer, config.PlayerDeathAnimationId.Value); Plugin.Log.LogInfo((object)$"{kind} door crushed local player."); } else if (Plugin.Verbose) { Vector3 val = zone.ToLocal(((Component)localPlayer).transform.position); Plugin.Log.LogInfo((object)($"{kind} door closed: local player not in doorway. " + $"Offset from zone (width={val.x:F2}, height={val.y:F2}, through={val.z:F2}); " + $"half-extents={zone.HalfExtents}.")); } } } if (!config.KillMonsters.Value || !GameCompat.IsHost || !((Object)(object)RoundManager.Instance != (Object)null)) { return; } List<EnemyAI> spawnedEnemies = RoundManager.Instance.SpawnedEnemies; for (int i = 0; i < spawnedEnemies.Count; i++) { EnemyAI val2 = spawnedEnemies[i]; if (!((Object)(object)val2 == (Object)null) && EnemyWhitelist.CanBeCrushed(val2) && zone.Contains(((Component)val2).transform.position)) { GameCompat.KillEnemy(val2); Plugin.Log.LogInfo((object)$"{kind} door crushed enemy '{GameCompat.EnemyName(val2)}'."); } } } public static void ExecuteDamageTick(DoorKind kind, DoorZone zone, ref float accumulator) { //IL_00cb: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) ModConfig config = Plugin.Config; if (!config.Enabled.Value || !config.KillPlayers.Value || !AppliesTo(kind) || ((Object)(object)LethalDoorsSession.Instance != (Object)null && LethalDoorsSession.Instance.InSafePeriod)) { return; } PlayerControllerB localPlayer = GameCompat.LocalPlayer; if (!GameCompat.IsPlayerAlive(localPlayer)) { return; } if (!zone.Contains(((Component)localPlayer).transform.position)) { accumulator = 0f; return; } accumulator += (float)config.DamageAmount.Value * Time.deltaTime; if (accumulator < 1f) { return; } int num = Mathf.FloorToInt(accumulator); accumulator -= num; try { localPlayer.DamagePlayer(num, true, true, (CauseOfDeath)17, config.PlayerDeathAnimationId.Value, false, default(Vector3)); } catch { try { localPlayer.DamagePlayer(num, true, true, (CauseOfDeath)0, 0, false, default(Vector3)); } catch { } } } private static bool AppliesTo(DoorKind kind) { return Plugin.Config.AffectedDoors.Value switch { AffectedDoors.ShipDoor => kind == DoorKind.Ship, AffectedDoors.TerminalDoors => kind == DoorKind.Terminal, _ => true, }; } } public readonly struct DoorZone { public readonly Vector3 Center; public readonly Quaternion Rot; public readonly Vector3 HalfExtents; public DoorZone(Vector3 center, Quaternion rot, Vector3 halfExtents) { //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_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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Center = center; Rot = rot; HalfExtents = halfExtents; } public Vector3 ToLocal(Vector3 world) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //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_0017: Unknown result type (might be due to invalid IL or missing references) return Quaternion.Inverse(Rot) * (world - Center); } public bool Contains(Vector3 world) { //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_0020: 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) Vector3 val = ToLocal(world); if (Mathf.Abs(val.x) <= HalfExtents.x && Mathf.Abs(val.y) <= HalfExtents.y) { return Mathf.Abs(val.z) <= HalfExtents.z; } return false; } public static DoorZone FromThroughAxis(Vector3 center, Vector3 throughAxis, float width, float height, float thickness) { //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_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_002e: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Vector3 val = throughAxis; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { val = Vector3.forward; } Quaternion rot = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); return new DoorZone(center, rot, new Vector3(width * 0.5f, height * 0.5f, thickness * 0.5f)); } } internal static class EnemyWhitelist { private static string[] _excluded = Array.Empty<string>(); private static string _cachedRaw; private static void RefreshCache() { string text = Plugin.Config.ExcludedEnemies.Value ?? string.Empty; if (!(text == _cachedRaw)) { _cachedRaw = text; _excluded = (from s in text.Split(',') select s.Trim() into s where s.Length > 0 select s).ToArray(); } } public static bool CanBeCrushed(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null || GameCompat.IsEnemyDead(enemy)) { return false; } RefreshCache(); if (_excluded.Length == 0) { return true; } string text = GameCompat.EnemyName(enemy); if (string.IsNullOrEmpty(text)) { return true; } for (int i = 0; i < _excluded.Length; i++) { if (text.IndexOf(_excluded[i], StringComparison.OrdinalIgnoreCase) >= 0) { return false; } } return true; } } public sealed class LethalDoorsSession : MonoBehaviour { private float _landedTime = -9999f; private bool _prevLanded; public static LethalDoorsSession Instance { get; private set; } public bool InSafePeriod { get { float value = Plugin.Config.SafeZoneSeconds.Value; if (value <= 0f) { return false; } return Time.time - _landedTime < value; } } private void Awake() { Instance = this; } private void Update() { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null)) { bool shipHasLanded = instance.shipHasLanded; if (shipHasLanded && !_prevLanded) { _landedTime = Time.time; } _prevLanded = shipHasLanded; TerminalDoorCrush.Tick(); AlliedTraps.Tick(); } } } internal static class TerminalDoorCrush { private sealed class Tracked { public TerminalAccessibleObject Door; public float CloseTime; public float DotAccumulator; } private static readonly List<Tracked> _tracked = new List<Tracked>(); public static void OnClosed(TerminalAccessibleObject door) { if ((Object)(object)door == (Object)null) { return; } for (int i = 0; i < _tracked.Count; i++) { if ((Object)(object)_tracked[i].Door == (Object)(object)door) { _tracked[i].CloseTime = Time.time; _tracked[i].DotAccumulator = 0f; return; } } _tracked.Add(new Tracked { Door = door, CloseTime = Time.time }); if (Plugin.Verbose) { Plugin.Log.LogInfo((object)("Terminal door '" + ((Object)((Component)door).gameObject).name + "' closing — tracking for crush.")); } } public static void Tick() { if (_tracked.Count == 0) { return; } float value = Plugin.Config.TerminalDoorCloseSeconds.Value; for (int num = _tracked.Count - 1; num >= 0; num--) { Tracked tracked = _tracked[num]; if ((Object)(object)tracked.Door == (Object)null || tracked.Door.isDoorOpen) { _tracked.RemoveAt(num); } else if (!(Time.time - tracked.CloseTime < value)) { if (Plugin.Config.DamageMode.Value == DamageMode.DamageOverTime) { DoorCrushManager.ExecuteDamageTick(DoorKind.Terminal, BuildTerminalZone(tracked.Door), ref tracked.DotAccumulator); } else { DoorCrushManager.ExecuteCrush(DoorKind.Terminal, BuildTerminalZone(tracked.Door)); _tracked.RemoveAt(num); } } } } public static void Clear() { _tracked.Clear(); } private static DoorZone BuildTerminalZone(TerminalAccessibleObject door) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0037: 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) ModConfig config = Plugin.Config; Vector3 center = ((Component)door).transform.position + Vector3.up * (config.DoorwayHeight.Value * 0.25f); Vector3 forward = ((Component)door).transform.forward; return DoorZone.FromThroughAxis(center, forward, config.DoorwayWidth.Value, config.DoorwayHeight.Value, config.DoorwayThickness.Value); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }