using 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.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalDoors.Doors; 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.0.9.0")] [assembly: AssemblyInformationalVersion("1.0.9+1735c2a96fa92bcc316f6a898981e973ef3ac9ff")] [assembly: AssemblyProduct("Terminal that can kill")] [assembly: AssemblyTitle("Solon.TerminalThatCanKill")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.9.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 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 void SustainBerserk(Turret turret, float minTimer) { if ((Object)(object)turret == (Object)null || !IsHost) { return; } try { turret.berserkTimer = Mathf.Max(turret.berserkTimer, minTimer); } catch { } } 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(); 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 Enabled; public readonly ConfigEntry KillPlayers; public readonly ConfigEntry KillMonsters; public readonly ConfigEntry AffectedDoors; public readonly ConfigEntry SafeZoneSeconds; public readonly ConfigEntry DamageMode; public readonly ConfigEntry DamageAmount; public readonly ConfigEntry ShipDoorCloseSeconds; public readonly ConfigEntry TerminalDoorCloseSeconds; public readonly ConfigEntry DoorwayThickness; public readonly ConfigEntry DoorwayWidth; public readonly ConfigEntry DoorwayHeight; public readonly ConfigEntry PlayerDeathAnimationId; public readonly ConfigEntry ExcludedEnemies; public readonly ConfigEntry EnableRemoteControl; public readonly ConfigEntry MineErrorChance; public readonly ConfigEntry TurretErrorChance; public readonly ConfigEntry MineErrorDamageMultiplier; public readonly ConfigEntry TurretRampageDuration; public readonly ConfigEntry TurretFlipOnBerserkExit; public readonly ConfigEntry TurretFlipSmoothDuration; public readonly ConfigEntry MineErrorChainRadius; public readonly ConfigEntry MineWarnBeforeDetonate; public ModConfig(ConfigFile cfg) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Expected O, but got Unknown //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Expected O, but got Unknown //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Expected O, but got Unknown //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Expected O, but got Unknown //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Expected O, but got Unknown //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Expected O, but got Unknown //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Expected O, but got Unknown Enabled = cfg.Bind("Doors", "Enabled", true, "Master switch for the deadly-doors mechanic."); KillPlayers = cfg.Bind("Doors", "KillPlayers", true, "Kill players caught in a closing door's threshold."); KillMonsters = cfg.Bind("Doors", "KillMonsters", false, "Kill monsters caught in a closing door's threshold (only enemies that can enter the ship)."); AffectedDoors = cfg.Bind("Doors", "AffectedDoors", LethalDoors.AffectedDoors.Both, "Which doors are deadly: ShipDoor, TerminalDoors or Both."); SafeZoneSeconds = cfg.Bind("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(0f, 120f), Array.Empty())); DamageMode = cfg.Bind("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("Doors", "DamageAmount", 100, new ConfigDescription("Damage-per-second applied to players in DamageOverTime mode (ignored for InstantKill).", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); ShipDoorCloseSeconds = cfg.Bind("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("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("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(0.2f, 6f), Array.Empty())); DoorwayWidth = cfg.Bind("Doors.Advanced", "DoorwayWidth", 3f, new ConfigDescription("Width (metres) of the kill slab across the doorway opening.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 8f), Array.Empty())); DoorwayHeight = cfg.Bind("Doors.Advanced", "DoorwayHeight", 5f, new ConfigDescription("Height (metres) of the kill slab. Kept generous so it never misses vertically.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 12f), Array.Empty())); PlayerDeathAnimationId = cfg.Bind("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("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("RemoteControl", "EnableRemoteControl", true, "Replace terminal 'disable mine/turret' with detonate/rampage. False = vanilla behaviour (disable)."); MineErrorChance = cfg.Bind("RemoteControl", "MineErrorChance", 0.15f, new ConfigDescription("Chance (0..1) that a remote MINE command critically malfunctions (chain detonation).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); TurretErrorChance = cfg.Bind("RemoteControl", "TurretErrorChance", 0.15f, new ConfigDescription("Chance (0..1) that a remote TURRET command critically malfunctions (sustained rampage).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); MineErrorDamageMultiplier = cfg.Bind("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(1f, 5f), Array.Empty())); TurretRampageDuration = cfg.Bind("RemoteControl", "TurretRampageDuration", 5f, new ConfigDescription("Seconds a turret keeps re-triggering its berserk state on an error (chaotic spin-fire).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 30f), Array.Empty())); TurretFlipOnBerserkExit = cfg.Bind("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("RemoteControl", "TurretFlipSmoothDuration", 0.5f, new ConfigDescription("Seconds the 180° head turn takes on berserk exit. 0 = instant snap.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); MineErrorChainRadius = cfg.Bind("RemoteControl.Advanced", "MineErrorChainRadius", 8f, "Base radius (metres) used to find neighbouring mines for the chain reaction on a mine error."); MineWarnBeforeDetonate = cfg.Bind("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.0.9")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "Solon.TerminalThatCanKill"; public const string PluginName = "Terminal that can kill"; public const string PluginVersion = "1.0.9"; 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; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Config = new ModConfig(((BaseUnityPlugin)this).Config); try { _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"Terminal that can kill v1.0.9 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.0.9"; } } namespace LethalDoors.Traps { internal static class RemoteControlManager { public static void HandleMine(Landmine mine) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mine == (Object)null) { return; } bool flag = Random.value < Plugin.Config.MineErrorChance.Value; if (!flag && Plugin.Config.MineWarnBeforeDetonate.Value) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(WarnThenDetonate(mine)); Plugin.Log.LogInfo((object)"Remote mine: armed (warning beep)."); return; } GameCompat.DetonateMine(mine); if (flag) { Plugin.Log.LogWarning((object)"Remote mine CRITICAL ERROR — chain detonation!"); ChainDetonateNearby(((Component)mine).transform.position); } else { Plugin.Log.LogInfo((object)"Remote mine: detonated."); } } 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(); 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 void HandleTurret(Turret turret) { if (!((Object)(object)turret == (Object)null)) { bool num = Random.value < Plugin.Config.TurretErrorChance.Value; GameCompat.BerserkTurret(turret); if (num) { Plugin.Log.LogWarning((object)"Remote turret CRITICAL ERROR — sustained rampage!"); ((MonoBehaviour)Plugin.Instance).StartCoroutine(Rampage(turret, Plugin.Config.TurretRampageDuration.Value)); } else { Plugin.Log.LogInfo((object)"Remote turret: berserk."); } } } private static IEnumerator Rampage(Turret turret, float duration) { float end = Time.time + duration; while (Time.time < end && !((Object)(object)turret == (Object)null)) { GameCompat.SustainBerserk(turret, 3f); GameCompat.BerserkTurret(turret); yield return (object)new WaitForSeconds(1.5f); } } } } namespace LethalDoors.Patches { [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; [HarmonyPostfix] private static void Postfix(HangarShipDoor __instance) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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; return; } _closeTimer = ((_closeTimer < 0f) ? 0f : (_closeTimer + Time.deltaTime)); bool flag = false; Animator shipDoorsAnimator = __instance.shipDoorsAnimator; if ((Object)(object)shipDoorsAnimator != (Object)null) { AnimatorStateInfo currentAnimatorStateInfo = shipDoorsAnimator.GetCurrentAnimatorStateInfo(0); flag = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("ShipDoorClose"); } if (flag || _closeTimer >= Plugin.Config.ShipDoorCloseSeconds.Value) { DoorZone zone = BuildShipZone(__instance); if (Plugin.Config.DamageMode.Value == DamageMode.DamageOverTime) { DoorCrushManager.ExecuteDamageTick(DoorKind.Ship, zone, ref _dotAccumulator); } else if (!_crushed) { _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); } } [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(); } TerminalDoorCrush.Clear(); TurretHeadFlipPatch.ClearStates(); } } [HarmonyPatch(typeof(TerminalAccessibleObject), "CallFunctionFromTerminal")] internal static class TerminalAccessibleObject_CallFunction_Patch { [HarmonyPrefix] private static bool Prefix(TerminalAccessibleObject __instance) { try { if (__instance.isBigDoor) { return true; } if (!Plugin.Config.EnableRemoteControl.Value) { return true; } Landmine val = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent() ?? ((Component)__instance).GetComponentInChildren(); if ((Object)(object)val != (Object)null) { RemoteControlManager.HandleMine(val); return false; } Turret val2 = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent() ?? ((Component)__instance).GetComponentInChildren(); if ((Object)(object)val2 != (Object)null) { RemoteControlManager.HandleTurret(val2); return false; } } 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 && Plugin.Config.Enabled.Value && Plugin.Config.AffectedDoors.Value != AffectedDoors.ShipDoor && __state && !open) { TerminalDoorCrush.OnClosed(__instance); } } } [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 _states = new Dictionary(); public static void ClearStates() { _states.Clear(); } [HarmonyPostfix] private static void Postfix(Turret __instance) { if (!Plugin.Config.TurretFlipOnBerserkExit.Value) { return; } try { Process(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"Turret head flip error: {arg}"); } } private static void Process(Turret turret) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected I4, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) if (!_states.TryGetValue(turret, out var value)) { value = new State { Head = FindHead(turret), PrevMode = (int)turret.turretMode }; _states[turret] = value; } if ((Object)(object)value.Head == (Object)null) { return; } int num = (int)turret.turretMode; if (value.PrevMode != 3 && num == 3) { value.RememberedYaw = value.Head.localEulerAngles.y; value.Animating = false; } else if (value.PrevMode == 3 && num != 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 = num; if (num == 3) { return; } if (value.Animating) { value.AnimTime += Time.deltaTime; float num2 = Mathf.Clamp01(value.AnimTime / value.AnimDuration); SetYaw(value.Head, Mathf.LerpAngle(value.AnimFromYaw, value.AnimToYaw, num2)); if (num2 >= 1f) { value.Animating = false; } } else if (value.HasResting && num == 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_0008: 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_0015: Unknown result type (might be due to invalid IL or missing references) Vector3 localEulerAngles = head.localEulerAngles; head.localEulerAngles = new Vector3(localEulerAngles.x, yaw, localEulerAngles.z); } 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(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_00a1: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_00dc: 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_0168: 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 { 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 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(); 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().ToLowerInvariant() 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(); string text = GameCompat.EnemyName(enemy).ToLowerInvariant(); string[] excluded = _excluded; foreach (string value in excluded) { if (text.Contains(value)) { 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(); } } } internal static class TerminalDoorCrush { private sealed class Tracked { public TerminalAccessibleObject Door; public float CloseTime; public bool Crushed; public float DotAccumulator; } private static readonly List _tracked = new List(); 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].Crushed = false; _tracked[i].DotAccumulator = 0f; return; } } _tracked.Add(new Tracked { Door = door, CloseTime = Time.time }); 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)) { DoorZone zone = BuildTerminalZone(tracked.Door); if (Plugin.Config.DamageMode.Value == DamageMode.DamageOverTime) { DoorCrushManager.ExecuteDamageTick(DoorKind.Terminal, zone, ref tracked.DotAccumulator); } else if (!tracked.Crushed) { tracked.Crushed = true; DoorCrushManager.ExecuteCrush(DoorKind.Terminal, zone); } } } } 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) { } } }