Decompiled source of Terminal that can kill v1.5.0

BepInEx/plugins/Solon.TerminalThatCanKill.dll

Decompiled 3 weeks ago
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.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)
		{
		}
	}
}