Decompiled source of DynamicPressure v0.1.1

Dynamic Pressure.dll

Decompiled a day 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.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.LevelGeneration;
using PerfectRandom.Sulfur.Core.Units;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Dynamic Pressure")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Dynamic Pressure")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("bf243145-c378-4bad-8447-4747dbf03372")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Ryuka.Sulfur.DynamicPressure;

[BepInPlugin("ryuka.sulfur.dynamicpressure", "Dynamic Pressure", "0.1.0")]
public sealed class DynamicPressurePlugin : BaseUnityPlugin
{
	private struct SpawnPointChoice
	{
		public Vector3 position;

		public Room room;

		public float distanceToPlayer;

		public string source;
	}

	private sealed class Snapshot
	{
		public bool hasGameManager;

		public bool hasPlayer;

		public bool playerAlive;

		public bool inSafeZone;

		public string gameState = "Unknown";

		public string currentEnvironment = "Unknown";

		public float playerHp;

		public float playerTimeSinceDamage;

		public string playerRoomName = "null";

		public bool playerRoomIsEndRoom;

		public int aliveNpcs;

		public int originalHostileAlive;

		public int originalEngagedHostiles;

		public int nearbyOriginalHostiles;

		public int originalTargetingPlayer;

		public int modHostileAlive;

		public int nearbyModHostiles;

		public int modTargetingPlayer;

		public int originalPressure;

		public int modPressure;

		public int currentPressure;

		public int targetPressure;

		public int deficit;

		public int candidateCount;

		public string candidateSource = "None";

		public int spawnedThisLevel;

		public int spawnedSinceLastOriginalKill;

		public float timeSinceLastOriginalKill;

		public float timeSinceLastModKill;

		public float nextSpawnIn;

		public bool wouldDelayOnAllEnemiesDead;
	}

	[CompilerGenerated]
	private sealed class <ReportPlayerPositionLater>d__70 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Npc npc;

		public DynamicPressureSpawnMarker marker;

		public DynamicPressurePlugin <>4__this;

		private GameManager <gm>5__1;

		private Exception <e>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <ReportPlayerPositionLater>d__70(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<gm>5__1 = null;
			<e>5__2 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = (object)new WaitForSeconds(0.5f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				<gm>5__1 = StaticInstance<GameManager>.Instance;
				if ((Object)(object)<gm>5__1 == (Object)null || (Object)(object)<gm>5__1.PlayerObject == (Object)null || (Object)(object)<gm>5__1.PlayerUnit == (Object)null)
				{
					<>4__this._lastAggroReport = "Failed: no player";
					return false;
				}
				if ((Object)(object)npc == (Object)null || !((Unit)npc).IsAlive || (Object)(object)npc.AiAgent == (Object)null)
				{
					<>4__this._lastAggroReport = "Failed: invalid npc";
					return false;
				}
				try
				{
					npc.AiAgent.ReportLastSeen(<gm>5__1.PlayerObject);
					marker.aggroReported = true;
					marker.lastAggroReportTime = Time.time;
					marker.targetingPlayerAfterReport = <>4__this.SafeTargetIsPlayer(npc);
					marker.hasKnownPlayerPositionAfterReport = <>4__this.SafeHasKnownPlayerPosition(npc, <gm>5__1.PlayerUnit);
					<>4__this._lastAggroReport = ((marker.targetingPlayerAfterReport || marker.hasKnownPlayerPositionAfterReport) ? "Success" : "Reported but not targeting yet");
					<>4__this.AddEvent("ReportLastSeen: " + <>4__this._lastAggroReport);
				}
				catch (Exception ex)
				{
					<e>5__2 = ex;
					<>4__this._lastAggroReport = "Exception: " + <e>5__2.GetType().Name;
					((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)<e>5__2);
				}
				return false;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	public const string PluginGuid = "ryuka.sulfur.dynamicpressure";

	public const string PluginName = "Dynamic Pressure";

	public const string PluginVersion = "0.1.0";

	private static DynamicPressurePlugin _instance;

	private Harmony _harmony;

	private ConfigEntry<bool> _enableMod;

	private ConfigEntry<bool> _enableAutoSpawn;

	private ConfigEntry<int> _pressureStyle;

	private ConfigEntry<bool> _enableOverlay;

	private ConfigEntry<Key> _overlayToggleKey;

	private ConfigEntry<Key> _manualSpawnKey;

	private ConfigEntry<float> _scanInterval;

	private ConfigEntry<float> _enemyScanRadius;

	private ConfigEntry<float> _closeEnemyDistance;

	private ConfigEntry<float> _lowHealthStopThreshold;

	private ConfigEntry<float> _recentDamageStopWindow;

	private ConfigEntry<int> _minOriginalEngagedHostilesForSpawn;

	private ConfigEntry<float> _cooldownAfterModKillOnly;

	private ConfigEntry<float> _maxSecondsWithoutOriginalKill;

	private ConfigEntry<int> _maxModSpawnsPerOriginalEngaged;

	private ConfigEntry<bool> _allowEnvironmentFallbackCandidates;

	private ConfigEntry<float> _spawnDistanceMin;

	private ConfigEntry<float> _spawnDistanceMax;

	private ConfigEntry<float> _style1Cooldown;

	private ConfigEntry<float> _style2Cooldown;

	private ConfigEntry<float> _style3Cooldown;

	private ConfigEntry<int> _style1TargetPressure;

	private ConfigEntry<int> _style2TargetPressure;

	private ConfigEntry<int> _style3TargetPressure;

	private ConfigEntry<int> _style1MaxSpawnPerWave;

	private ConfigEntry<int> _style2MaxSpawnPerWave;

	private ConfigEntry<int> _style3MaxSpawnPerWave;

	private ConfigEntry<int> _style1MaxModAlive;

	private ConfigEntry<int> _style2MaxModAlive;

	private ConfigEntry<int> _style3MaxModAlive;

	private ConfigEntry<int> _style1MaxModPerRoom;

	private ConfigEntry<int> _style2MaxModPerRoom;

	private ConfigEntry<int> _style3MaxModPerRoom;

	private ConfigEntry<int> _style1MaxModPerLevel;

	private ConfigEntry<int> _style2MaxModPerLevel;

	private ConfigEntry<int> _style3MaxModPerLevel;

	private readonly List<UnitSO> _candidatePool = new List<UnitSO>();

	private readonly List<string> _recentEvents = new List<string>();

	private readonly Dictionary<int, int> _modSpawnedPerRoom = new Dictionary<int, int>();

	private readonly HashSet<int> _deadNpcIds = new HashSet<int>();

	private object _lastGraphContext;

	private float _nextScanTime;

	private float _nextSpawnTime;

	private bool _spawnInProgress;

	private bool _inLevelTransition;

	private int _waveId;

	private int _spawnedThisLevel;

	private int _spawnedSinceLastOriginalKill;

	private float _lastOriginalKillTime = -9999f;

	private float _lastModKillTime = -9999f;

	private Snapshot _snapshot = new Snapshot();

	private SpawnBlockReason _lastBlockReason = SpawnBlockReason.None;

	private string _lastDecision = "None";

	private string _lastSpawnSummary = "None";

	private string _lastAggroReport = "None";

	private void Awake()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		_instance = this;
		BindConfig();
		_harmony = new Harmony("ryuka.sulfur.dynamicpressure");
		TryPatchTransitions();
		TryPatchNpcDie();
		Log("Loaded.");
	}

	private void OnDestroy()
	{
		try
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
		if ((Object)(object)_instance == (Object)(object)this)
		{
			_instance = null;
		}
	}

	private void BindConfig()
	{
		_enableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, "Enable Dynamic Pressure.");
		_enableAutoSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableAutoSpawn", true, "Enable automatic dynamic pressure spawning. Keep false for first debug testing.");
		_pressureStyle = ((BaseUnityPlugin)this).Config.Bind<int>("General", "PressureStyle", 1, "1 = Light, 2 = Heavy, 3 = Nightmare.");
		_enableOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug Overlay", "EnableOverlay", false, "Show debug overlay.");
		_overlayToggleKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Debug Overlay", "OverlayToggleKey", (Key)102, "Toggle overlay key.");
		_manualSpawnKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Debug Overlay", "ManualSpawnKey", (Key)101, "Manual debug spawn key.");
		_scanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Pressure", "ScanInterval", 1f, "Seconds between pressure scans.");
		_enemyScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Pressure", "EnemyScanRadius", 25f, "Nearby enemy scan radius.");
		_closeEnemyDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Pressure", "CloseEnemyDistance", 8f, "Enemies closer than this add extra pressure.");
		_lowHealthStopThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "LowHealthStopThreshold", 0.35f, "Do not spawn if player health is below this normalized value.");
		_recentDamageStopWindow = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "RecentDamageStopWindow", 4f, "Do not spawn if player was damaged within this many seconds.");
		_minOriginalEngagedHostilesForSpawn = ((BaseUnityPlugin)this).Config.Bind<int>("Anti Loop", "MinOriginalEngagedHostilesForSpawn", 1, "Require this many engaged original hostiles before spawning mod enemies.");
		_cooldownAfterModKillOnly = ((BaseUnityPlugin)this).Config.Bind<float>("Anti Loop", "CooldownAfterModKillOnly", 8f, "If only mod enemies are dying, pause spawning for this many seconds.");
		_maxSecondsWithoutOriginalKill = ((BaseUnityPlugin)this).Config.Bind<float>("Anti Loop", "MaxSecondsWithoutOriginalKill", 30f, "Stop spawning if no original enemy has died for this long after mod spawns.");
		_maxModSpawnsPerOriginalEngaged = ((BaseUnityPlugin)this).Config.Bind<int>("Anti Loop", "MaxModSpawnsPerOriginalEngaged", 2, "Soft budget: each engaged original enemy can support this many mod spawns.");
		_allowEnvironmentFallbackCandidates = ((BaseUnityPlugin)this).Config.Bind<bool>("Candidates", "AllowEnvironmentFallbackCandidates", false, "Use current environment enemy metadata if current level alive NPC candidate pool is empty.");
		_spawnDistanceMin = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "SpawnDistanceMin", 8f, "Minimum spawn distance from player.");
		_spawnDistanceMax = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "SpawnDistanceMax", 60f, "Maximum spawn distance from player.");
		_style1TargetPressure = ((BaseUnityPlugin)this).Config.Bind<int>("Style 1 - Light", "TargetPressure", 6, "Target pressure.");
		_style1Cooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Style 1 - Light", "SpawnCooldown", 16f, "Spawn cooldown.");
		_style1MaxSpawnPerWave = ((BaseUnityPlugin)this).Config.Bind<int>("Style 1 - Light", "MaxSpawnPerWave", 1, "Max spawns per wave.");
		_style1MaxModAlive = ((BaseUnityPlugin)this).Config.Bind<int>("Style 1 - Light", "MaxModSpawnedAlive", 2, "Max alive mod-spawned NPCs.");
		_style1MaxModPerRoom = ((BaseUnityPlugin)this).Config.Bind<int>("Style 1 - Light", "MaxModSpawnedPerRoom", 3, "Max mod spawns per room.");
		_style1MaxModPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Style 1 - Light", "MaxModSpawnedPerLevel", 8, "Max mod spawns per level.");
		_style2TargetPressure = ((BaseUnityPlugin)this).Config.Bind<int>("Style 2 - Heavy", "TargetPressure", 9, "Target pressure.");
		_style2Cooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Style 2 - Heavy", "SpawnCooldown", 10f, "Spawn cooldown.");
		_style2MaxSpawnPerWave = ((BaseUnityPlugin)this).Config.Bind<int>("Style 2 - Heavy", "MaxSpawnPerWave", 1, "Max spawns per wave.");
		_style2MaxModAlive = ((BaseUnityPlugin)this).Config.Bind<int>("Style 2 - Heavy", "MaxModSpawnedAlive", 4, "Max alive mod-spawned NPCs.");
		_style2MaxModPerRoom = ((BaseUnityPlugin)this).Config.Bind<int>("Style 2 - Heavy", "MaxModSpawnedPerRoom", 5, "Max mod spawns per room.");
		_style2MaxModPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Style 2 - Heavy", "MaxModSpawnedPerLevel", 16, "Max mod spawns per level.");
		_style3TargetPressure = ((BaseUnityPlugin)this).Config.Bind<int>("Style 3 - Nightmare", "TargetPressure", 13, "Target pressure.");
		_style3Cooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Style 3 - Nightmare", "SpawnCooldown", 7f, "Spawn cooldown.");
		_style3MaxSpawnPerWave = ((BaseUnityPlugin)this).Config.Bind<int>("Style 3 - Nightmare", "MaxSpawnPerWave", 2, "Max spawns per wave.");
		_style3MaxModAlive = ((BaseUnityPlugin)this).Config.Bind<int>("Style 3 - Nightmare", "MaxModSpawnedAlive", 6, "Max alive mod-spawned NPCs.");
		_style3MaxModPerRoom = ((BaseUnityPlugin)this).Config.Bind<int>("Style 3 - Nightmare", "MaxModSpawnedPerRoom", 8, "Max mod spawns per room.");
		_style3MaxModPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Style 3 - Nightmare", "MaxModSpawnedPerLevel", 28, "Max mod spawns per level.");
	}

	private void Update()
	{
		HandleKeys();
		if (!_enableMod.Value)
		{
			_lastDecision = "BLOCKED";
			_lastBlockReason = SpawnBlockReason.Disabled;
			return;
		}
		GameManager instance = StaticInstance<GameManager>.Instance;
		if ((Object)(object)instance != (Object)null && (Object)(object)instance.graphContext != (Object)null && _lastGraphContext != instance.graphContext)
		{
			_lastGraphContext = instance.graphContext;
			ResetRuntimeState("New graphContext");
		}
		if (Time.time >= _nextScanTime)
		{
			_nextScanTime = Time.time + Mathf.Max(0.2f, _scanInterval.Value);
			ScanPressure();
		}
		if (_enableAutoSpawn.Value && !_spawnInProgress && Time.time >= _nextSpawnTime)
		{
			TryAutoSpawn();
		}
	}

	private void HandleKeys()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		Keyboard current = Keyboard.current;
		if (current != null)
		{
			if (((ButtonControl)current[_overlayToggleKey.Value]).wasPressedThisFrame)
			{
				_enableOverlay.Value = !_enableOverlay.Value;
			}
			if (((ButtonControl)current[_manualSpawnKey.Value]).wasPressedThisFrame && !_spawnInProgress)
			{
				SpawnWaveAsync(1, "Manual debug spawn");
			}
		}
	}

	private void ScanPressure()
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		Snapshot snapshot = new Snapshot();
		_candidatePool.Clear();
		GameManager instance = StaticInstance<GameManager>.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			_snapshot = snapshot;
			_lastBlockReason = SpawnBlockReason.NoGameManager;
			return;
		}
		snapshot.hasGameManager = true;
		GameState gameState = instance.gameState;
		snapshot.gameState = ((object)(GameState)(ref gameState)).ToString();
		snapshot.inSafeZone = instance.InSafeZone;
		snapshot.currentEnvironment = (((Object)(object)instance.currentEnvironment != (Object)null) ? ((object)(WorldEnvironmentIds)(ref instance.currentEnvironment.id)).ToString() : "null");
		if ((Object)(object)instance.PlayerUnit == (Object)null || (Object)(object)instance.PlayerObject == (Object)null)
		{
			_snapshot = snapshot;
			_lastBlockReason = SpawnBlockReason.NoPlayer;
			return;
		}
		Unit playerUnit = instance.PlayerUnit;
		Room unitRoom = GetUnitRoom(playerUnit);
		snapshot.hasPlayer = true;
		snapshot.playerAlive = playerUnit.IsAlive;
		snapshot.playerHp = SafeGetPlayerHp(playerUnit);
		snapshot.playerTimeSinceDamage = playerUnit.TimeSinceLastDamageTaken;
		snapshot.playerRoomName = (((Object)(object)unitRoom != (Object)null) ? ((Object)unitRoom).name : "null");
		snapshot.playerRoomIsEndRoom = (Object)(object)unitRoom != (Object)null && unitRoom.IsEndRoom;
		List<Npc> aliveNpcs = instance.aliveNpcs;
		snapshot.aliveNpcs = aliveNpcs?.Count ?? 0;
		if (aliveNpcs != null)
		{
			for (int i = 0; i < aliveNpcs.Count; i++)
			{
				Npc val = aliveNpcs[i];
				if (!IsValidAliveNpc(val))
				{
					continue;
				}
				DynamicPressureSpawnMarker component = ((Component)val).GetComponent<DynamicPressureSpawnMarker>();
				bool flag = (Object)(object)component != (Object)null;
				if (!IsHostileToPlayer(val))
				{
					continue;
				}
				float num = Vector3.Distance(((Component)val).transform.position, instance.PlayerPosition);
				bool flag2 = num <= _enemyScanRadius.Value;
				bool flag3 = SafeTargetIsPlayer(val);
				bool flag4 = SafeHasKnownPlayerPosition(val, instance.PlayerUnit);
				bool flag5 = IsSameOrConnectedRoom(GetUnitRoom((Unit)(object)val), unitRoom);
				bool flag6 = flag2 || flag3 || flag4 || flag5;
				int num2 = CalculateNpcPressure(val, num, flag3, flag4);
				if (flag)
				{
					snapshot.modHostileAlive++;
					if (flag2)
					{
						snapshot.nearbyModHostiles++;
						snapshot.modPressure += num2;
					}
					if (flag3)
					{
						snapshot.modTargetingPlayer++;
					}
					continue;
				}
				snapshot.originalHostileAlive++;
				if (flag6)
				{
					snapshot.originalEngagedHostiles++;
				}
				if (flag2)
				{
					snapshot.nearbyOriginalHostiles++;
					snapshot.originalPressure += num2;
				}
				if (flag3)
				{
					snapshot.originalTargetingPlayer++;
				}
				TryAddCandidateFromNpc(val);
			}
		}
		if (_candidatePool.Count == 0 && _allowEnvironmentFallbackCandidates.Value)
		{
			AddEnvironmentFallbackCandidates(instance);
			snapshot.candidateSource = ((_candidatePool.Count > 0) ? "EnvironmentMetadata" : "None");
		}
		else
		{
			snapshot.candidateSource = ((_candidatePool.Count > 0) ? "CurrentLevelAliveNpcs" : "None");
		}
		snapshot.candidateCount = _candidatePool.Count;
		snapshot.targetPressure = GetTargetPressure();
		snapshot.currentPressure = snapshot.originalPressure + snapshot.modPressure;
		snapshot.deficit = snapshot.targetPressure - snapshot.currentPressure;
		snapshot.spawnedThisLevel = _spawnedThisLevel;
		snapshot.spawnedSinceLastOriginalKill = _spawnedSinceLastOriginalKill;
		snapshot.timeSinceLastOriginalKill = Time.time - _lastOriginalKillTime;
		snapshot.timeSinceLastModKill = Time.time - _lastModKillTime;
		snapshot.nextSpawnIn = Mathf.Max(0f, _nextSpawnTime - Time.time);
		snapshot.wouldDelayOnAllEnemiesDead = snapshot.originalHostileAlive == 0 && snapshot.modHostileAlive > 0;
		_snapshot = snapshot;
	}

	private void TryAutoSpawn()
	{
		if (!CanSpawn(out var blockReason, out var spawnCount))
		{
			_lastDecision = "BLOCKED";
			_lastBlockReason = blockReason;
		}
		else
		{
			SpawnWaveAsync(spawnCount, "Auto pressure deficit");
		}
	}

	private bool CanSpawn(out SpawnBlockReason blockReason, out int spawnCount)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		spawnCount = 0;
		GameManager instance = StaticInstance<GameManager>.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			blockReason = SpawnBlockReason.NoGameManager;
			return false;
		}
		if (_inLevelTransition)
		{
			blockReason = SpawnBlockReason.LevelTransition;
			return false;
		}
		if ((Object)(object)instance.PlayerUnit == (Object)null || (Object)(object)instance.PlayerObject == (Object)null)
		{
			blockReason = SpawnBlockReason.NoPlayer;
			return false;
		}
		GameState gameState = instance.gameState;
		if (((object)(GameState)(ref gameState)).ToString() != "Running")
		{
			blockReason = SpawnBlockReason.GameStateNotRunning;
			return false;
		}
		if (instance.InSafeZone)
		{
			blockReason = SpawnBlockReason.SafeZone;
			return false;
		}
		Unit playerUnit = instance.PlayerUnit;
		if (!playerUnit.IsAlive)
		{
			blockReason = SpawnBlockReason.PlayerDead;
			return false;
		}
		if (SafeGetPlayerHp(playerUnit) <= _lowHealthStopThreshold.Value)
		{
			blockReason = SpawnBlockReason.LowHealth;
			return false;
		}
		if (playerUnit.TimeSinceLastDamageTaken <= _recentDamageStopWindow.Value)
		{
			blockReason = SpawnBlockReason.RecentDamage;
			return false;
		}
		Room unitRoom = GetUnitRoom(playerUnit);
		if ((Object)(object)unitRoom != (Object)null && unitRoom.IsEndRoom)
		{
			blockReason = SpawnBlockReason.EndRoomBlocked;
			return false;
		}
		if (_snapshot.originalEngagedHostiles < _minOriginalEngagedHostilesForSpawn.Value)
		{
			blockReason = SpawnBlockReason.NoEngagedOriginalHostiles;
			return false;
		}
		if (_snapshot.currentPressure >= _snapshot.targetPressure)
		{
			blockReason = SpawnBlockReason.PressureAlreadyHigh;
			return false;
		}
		if (_snapshot.modHostileAlive >= GetMaxModAlive())
		{
			blockReason = SpawnBlockReason.MaxModSpawnedAlive;
			return false;
		}
		if (_spawnedThisLevel >= GetMaxModPerLevel())
		{
			blockReason = SpawnBlockReason.LevelSpawnBudgetExceeded;
			return false;
		}
		int num = Mathf.Max(1, _snapshot.originalEngagedHostiles) * Mathf.Max(1, _maxModSpawnsPerOriginalEngaged.Value);
		if (_spawnedSinceLastOriginalKill >= num)
		{
			blockReason = SpawnBlockReason.SpawnBudgetPerOriginalExceeded;
			return false;
		}
		if (_spawnedSinceLastOriginalKill > 0 && Time.time - _lastOriginalKillTime > _maxSecondsWithoutOriginalKill.Value)
		{
			blockReason = SpawnBlockReason.NoOriginalKillProgress;
			return false;
		}
		if (_lastModKillTime > _lastOriginalKillTime && Time.time - _lastModKillTime < _cooldownAfterModKillOnly.Value)
		{
			blockReason = SpawnBlockReason.RecentModKillOnly;
			return false;
		}
		if (_candidatePool.Count == 0)
		{
			blockReason = SpawnBlockReason.NoCandidateUnits;
			return false;
		}
		spawnCount = Mathf.Min(GetMaxSpawnPerWave(), Mathf.Max(1, _snapshot.deficit));
		spawnCount = Mathf.Min(spawnCount, GetMaxModAlive() - _snapshot.modHostileAlive);
		spawnCount = Mathf.Min(spawnCount, GetMaxModPerLevel() - _spawnedThisLevel);
		if (spawnCount <= 0)
		{
			blockReason = SpawnBlockReason.MaxModSpawnedAlive;
			return false;
		}
		blockReason = SpawnBlockReason.None;
		return true;
	}

	private async Task SpawnWaveAsync(int count, string reason)
	{
		if (_spawnInProgress)
		{
			return;
		}
		_spawnInProgress = true;
		_waveId++;
		int spawned = 0;
		try
		{
			ScanPressure();
			for (int i = 0; i < count; i++)
			{
				UnitSO unitSo = PickCandidate();
				if ((Object)(object)unitSo == (Object)null)
				{
					_lastDecision = "BLOCKED";
					_lastBlockReason = SpawnBlockReason.NoCandidateUnits;
					AddEvent("Spawn blocked: no candidate.");
					break;
				}
				if (!TryFindSpawnPoint(unitSo, out var spawnPoint))
				{
					_lastDecision = "BLOCKED";
					_lastBlockReason = SpawnBlockReason.NoValidNpcSpawnPoint;
					AddEvent("Spawn blocked: no valid NPCSpawn point.");
					break;
				}
				int roomId = GetRoomId(spawnPoint.room);
				_modSpawnedPerRoom.TryGetValue(roomId, out var roomCount);
				if (roomCount >= GetMaxModPerRoom())
				{
					_lastDecision = "BLOCKED";
					_lastBlockReason = SpawnBlockReason.RoomSpawnBudgetExceeded;
					AddEvent("Spawn blocked: room budget exceeded.");
					break;
				}
				if (!(await SpawnOneAsync(unitSo, spawnPoint, reason)))
				{
					_lastDecision = "FAILED";
					_lastBlockReason = SpawnBlockReason.SpawnAsyncFailed;
					break;
				}
				spawned++;
				spawnPoint = default(SpawnPointChoice);
			}
			if (spawned > 0)
			{
				_lastDecision = "SPAWNED";
				_lastBlockReason = SpawnBlockReason.None;
				_nextSpawnTime = Time.time + GetSpawnCooldown();
			}
		}
		catch (Exception ex)
		{
			Exception e = ex;
			_lastDecision = "FAILED";
			_lastBlockReason = SpawnBlockReason.SpawnAsyncFailed;
			AddEvent("Spawn exception: " + e.GetType().Name);
			((BaseUnityPlugin)this).Logger.LogError((object)e);
		}
		finally
		{
			_spawnInProgress = false;
			ScanPressure();
		}
	}

	private async Task<bool> SpawnOneAsync(UnitSO unitSo, SpawnPointChoice spawnPoint, string reason)
	{
		if ((Object)(object)unitSo == (Object)null)
		{
			return false;
		}
		Unit spawnedUnit = await unitSo.SpawnUnitAsync((MonoBehaviour)(object)this, spawnPoint.position, Quaternion.identity);
		if ((Object)(object)spawnedUnit == (Object)null)
		{
			return false;
		}
		Npc npc = (Npc)(object)((spawnedUnit is Npc) ? spawnedUnit : null);
		if ((Object)(object)npc == (Object)null)
		{
			npc = ((Component)spawnedUnit).GetComponent<Npc>();
		}
		if ((Object)(object)npc == (Object)null)
		{
			return false;
		}
		if ((Object)(object)spawnPoint.room != (Object)null)
		{
			((Unit)npc).currentRoom = spawnPoint.room;
			((Unit)npc).lastValidCurrentRoom = spawnPoint.room;
			((Unit)npc).lastRoomCalcPosition = ((Component)npc).transform.position;
		}
		DynamicPressureSpawnMarker marker = ((Component)npc).gameObject.AddComponent<DynamicPressureSpawnMarker>();
		marker.sourceUnitSo = unitSo;
		marker.spawnTime = Time.time;
		marker.spawnPosition = spawnPoint.position;
		marker.spawnRoom = spawnPoint.room;
		marker.spawnReason = reason;
		marker.waveId = _waveId;
		int roomId = GetRoomId(spawnPoint.room);
		_modSpawnedPerRoom.TryGetValue(roomId, out var roomCount);
		_modSpawnedPerRoom[roomId] = roomCount + 1;
		_spawnedThisLevel++;
		_spawnedSinceLastOriginalKill++;
		_lastSpawnSummary = string.Format("{0} at {1}, room={2}, source={3}", ((Object)unitSo).name, spawnPoint.position, ((Object)(object)spawnPoint.room != (Object)null) ? ((Object)spawnPoint.room).name : "null", spawnPoint.source);
		AddEvent("Spawned: " + ((Object)unitSo).name);
		((MonoBehaviour)this).StartCoroutine(ReportPlayerPositionLater(npc, marker));
		return true;
	}

	[IteratorStateMachine(typeof(<ReportPlayerPositionLater>d__70))]
	private IEnumerator ReportPlayerPositionLater(Npc npc, DynamicPressureSpawnMarker marker)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <ReportPlayerPositionLater>d__70(0)
		{
			<>4__this = this,
			npc = npc,
			marker = marker
		};
	}

	private UnitSO PickCandidate()
	{
		if (_candidatePool.Count == 0)
		{
			return null;
		}
		return _candidatePool[Random.Range(0, _candidatePool.Count)];
	}

	private void TryAddCandidateFromNpc(Npc npc)
	{
		if (!((Object)(object)npc == (Object)null) && !((Object)(object)((Unit)npc).unitSO == (Object)null))
		{
			UnitSO unitSO = ((Unit)npc).unitSO;
			if (IsValidCandidateUnitSo(unitSO) && !_candidatePool.Contains(unitSO))
			{
				_candidatePool.Add(unitSO);
			}
		}
	}

	private void AddEnvironmentFallbackCandidates(GameManager gm)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)gm == (Object)null || (Object)(object)gm.environmentsInOrder == (Object)null || (Object)(object)gm.currentEnvironment == (Object)null)
			{
				return;
			}
			Metadata metadata = gm.environmentsInOrder.GetMetadata(gm.currentEnvironment.id);
			if (metadata.availableEnemiesReadOnly == null)
			{
				return;
			}
			for (int i = 0; i < metadata.availableEnemiesReadOnly.Length; i++)
			{
				UnitSO asset = AssetAccess.GetAsset(metadata.availableEnemiesReadOnly[i]);
				if ((Object)(object)asset != (Object)null && IsValidCandidateUnitSo(asset) && !_candidatePool.Contains(asset))
				{
					_candidatePool.Add(asset);
				}
			}
		}
		catch (Exception ex)
		{
			AddEvent("Candidate fallback failed: " + ex.GetType().Name);
		}
	}

	private bool IsValidCandidateUnitSo(UnitSO so)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Invalid comparison between Unknown and I4
		if ((Object)(object)so == (Object)null)
		{
			return false;
		}
		if (so.isCivilian)
		{
			return false;
		}
		if (so.isProtectedNpc)
		{
			return false;
		}
		if (so.ExperienceOnKill <= 0)
		{
			return false;
		}
		if ((so.unitType & 8) > 0)
		{
			return false;
		}
		return true;
	}

	private bool TryFindSpawnPoint(UnitSO unitSo, out SpawnPointChoice result)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0231: Unknown result type (might be due to invalid IL or missing references)
		//IL_0254: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: 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_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Invalid comparison between Unknown and I4
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		result = default(SpawnPointChoice);
		GameManager instance = StaticInstance<GameManager>.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)instance.PlayerUnit == (Object)null || (Object)(object)instance.PlayerObject == (Object)null || (Object)(object)unitSo == (Object)null)
		{
			return false;
		}
		Vector3 playerPosition = instance.PlayerPosition;
		List<SpawnPointChoice> list = new List<SpawnPointChoice>();
		List<SpawnPointChoice> list2 = new List<SpawnPointChoice>();
		List<Room> orderedRooms = instance.orderedRooms;
		if (orderedRooms == null || orderedRooms.Count == 0)
		{
			return false;
		}
		for (int i = 0; i < orderedRooms.Count; i++)
		{
			Room val = orderedRooms[i];
			if (!IsValidSpawnRoom(val))
			{
				continue;
			}
			Data[] nPCSpawns = val.GetNPCSpawns();
			if (nPCSpawns == null || nPCSpawns.Length == 0)
			{
				continue;
			}
			foreach (Data val2 in nPCSpawns)
			{
				Room room = (((Object)(object)val2.room != (Object)null) ? val2.room : val);
				if (IsValidSpawnRoom(room) && (val2.usableByTypes & unitSo.unitType) != 0)
				{
					float num = Vector3.Distance(val2.position, playerPosition);
					SpawnPointChoice spawnPointChoice = default(SpawnPointChoice);
					spawnPointChoice.position = val2.position;
					spawnPointChoice.room = room;
					spawnPointChoice.distanceToPlayer = num;
					spawnPointChoice.source = "GameManager.orderedRooms NPCSpawn";
					SpawnPointChoice item = spawnPointChoice;
					if (num >= _spawnDistanceMin.Value && num <= _spawnDistanceMax.Value)
					{
						list.Add(item);
					}
					else if (num > _spawnDistanceMax.Value && num <= _spawnDistanceMax.Value * 2f)
					{
						list2.Add(item);
					}
				}
			}
		}
		if (list.Count > 0)
		{
			result = PickNearestRandomized(list, playerPosition);
			return true;
		}
		if (list2.Count > 0)
		{
			result = PickNearestRandomized(list2, playerPosition);
			result.source += " / loose distance";
			return true;
		}
		return false;
	}

	private bool IsValidSpawnRoom(Room room)
	{
		if ((Object)(object)room == (Object)null)
		{
			return false;
		}
		if (room.IsStartRoom)
		{
			return false;
		}
		if (room.IsEndRoom)
		{
			return false;
		}
		if (room.disallowEnemySpawn)
		{
			return false;
		}
		return true;
	}

	private SpawnPointChoice PickNearestRandomized(List<SpawnPointChoice> choices, Vector3 playerPos)
	{
		choices.Sort((SpawnPointChoice a, SpawnPointChoice b) => a.distanceToPlayer.CompareTo(b.distanceToPlayer));
		int num = Mathf.Min(choices.Count, 5);
		return choices[Random.Range(0, num)];
	}

	private int CalculateNpcPressure(Npc npc, float distance, bool targeting, bool knowsPlayer)
	{
		int num = 1;
		if ((Object)(object)npc != (Object)null && (Object)(object)((Unit)npc).unitSO != (Object)null)
		{
			num = Mathf.Clamp(((Unit)npc).unitSO.SpawnCost, 1, 4);
		}
		if (distance <= _closeEnemyDistance.Value)
		{
			num++;
		}
		if (targeting)
		{
			num += 2;
		}
		else if (knowsPlayer)
		{
			num++;
		}
		return num;
	}

	private bool IsValidAliveNpc(Npc npc)
	{
		return (Object)(object)npc != (Object)null && (Object)(object)((Component)npc).gameObject != (Object)null && ((Unit)npc).IsAlive;
	}

	private bool IsHostileToPlayer(Npc npc)
	{
		try
		{
			return (Object)(object)npc != (Object)null && ((Unit)npc).IsHostileTo((FactionIds)16);
		}
		catch
		{
			return false;
		}
	}

	private bool SafeTargetIsPlayer(Npc npc)
	{
		try
		{
			return (Object)(object)npc != (Object)null && npc.targetIsPlayer;
		}
		catch
		{
			return false;
		}
	}

	private bool SafeHasKnownPlayerPosition(Npc npc, Unit playerUnit)
	{
		try
		{
			return (Object)(object)npc != (Object)null && (Object)(object)npc.AiAgent != (Object)null && (Object)(object)playerUnit != (Object)null && npc.AiAgent.HasKnownPosition(playerUnit);
		}
		catch
		{
			return false;
		}
	}

	private float SafeGetPlayerHp(Unit playerUnit)
	{
		try
		{
			return ((Object)(object)playerUnit != (Object)null) ? playerUnit.GetNormalizedHealth() : 0f;
		}
		catch
		{
			return 0f;
		}
	}

	private Room GetUnitRoom(Unit unit)
	{
		if ((Object)(object)unit == (Object)null)
		{
			return null;
		}
		if ((Object)(object)unit.currentRoom != (Object)null)
		{
			return unit.currentRoom;
		}
		return unit.lastValidCurrentRoom;
	}

	private bool IsSameOrConnectedRoom(Room npcRoom, Room playerRoom)
	{
		if ((Object)(object)npcRoom == (Object)null || (Object)(object)playerRoom == (Object)null)
		{
			return false;
		}
		if ((Object)(object)npcRoom == (Object)(object)playerRoom)
		{
			return true;
		}
		if (playerRoom.connectedRooms == null)
		{
			return false;
		}
		for (int i = 0; i < playerRoom.connectedRooms.Length; i++)
		{
			if ((Object)(object)playerRoom.connectedRooms[i] == (Object)(object)npcRoom)
			{
				return true;
			}
		}
		return false;
	}

	private int GetRoomId(Room room)
	{
		return ((Object)(object)room != (Object)null) ? ((Object)room).GetInstanceID() : 0;
	}

	private int GetStyle()
	{
		return Mathf.Clamp(_pressureStyle.Value, 1, 3);
	}

	private int GetTargetPressure()
	{
		return GetStyle() switch
		{
			1 => _style1TargetPressure.Value, 
			2 => _style2TargetPressure.Value, 
			_ => _style3TargetPressure.Value, 
		};
	}

	private float GetSpawnCooldown()
	{
		return GetStyle() switch
		{
			1 => _style1Cooldown.Value, 
			2 => _style2Cooldown.Value, 
			_ => _style3Cooldown.Value, 
		};
	}

	private int GetMaxSpawnPerWave()
	{
		return GetStyle() switch
		{
			1 => _style1MaxSpawnPerWave.Value, 
			2 => _style2MaxSpawnPerWave.Value, 
			_ => _style3MaxSpawnPerWave.Value, 
		};
	}

	private int GetMaxModAlive()
	{
		return GetStyle() switch
		{
			1 => _style1MaxModAlive.Value, 
			2 => _style2MaxModAlive.Value, 
			_ => _style3MaxModAlive.Value, 
		};
	}

	private int GetMaxModPerRoom()
	{
		return GetStyle() switch
		{
			1 => _style1MaxModPerRoom.Value, 
			2 => _style2MaxModPerRoom.Value, 
			_ => _style3MaxModPerRoom.Value, 
		};
	}

	private int GetMaxModPerLevel()
	{
		return GetStyle() switch
		{
			1 => _style1MaxModPerLevel.Value, 
			2 => _style2MaxModPerLevel.Value, 
			_ => _style3MaxModPerLevel.Value, 
		};
	}

	private void ResetRuntimeState(string reason)
	{
		_candidatePool.Clear();
		_recentEvents.Clear();
		_modSpawnedPerRoom.Clear();
		_deadNpcIds.Clear();
		_waveId = 0;
		_spawnedThisLevel = 0;
		_spawnedSinceLastOriginalKill = 0;
		_lastOriginalKillTime = Time.time;
		_lastModKillTime = -9999f;
		_lastDecision = "Reset";
		_lastBlockReason = SpawnBlockReason.None;
		_lastSpawnSummary = "None";
		_lastAggroReport = "None";
		_inLevelTransition = false;
		_nextSpawnTime = Time.time + 3f;
		AddEvent("Reset: " + reason);
	}

	private static void OnNpcDiePostfix(Npc __instance)
	{
		if (!((Object)(object)_instance == (Object)null) && !((Object)(object)__instance == (Object)null))
		{
			_instance.HandleNpcDeath(__instance);
		}
	}

	private void HandleNpcDeath(Npc npc)
	{
		int instanceID = ((Object)npc).GetInstanceID();
		if (_deadNpcIds.Contains(instanceID))
		{
			return;
		}
		_deadNpcIds.Add(instanceID);
		bool flag = (Object)(object)((Component)npc).GetComponent<DynamicPressureSpawnMarker>() != (Object)null;
		if (IsHostileToPlayer(npc))
		{
			if (flag)
			{
				_lastModKillTime = Time.time;
				AddEvent("Mod NPC died: " + ((Object)npc).name);
			}
			else
			{
				_lastOriginalKillTime = Time.time;
				_spawnedSinceLastOriginalKill = 0;
				AddEvent("Original NPC died: " + ((Object)npc).name);
			}
		}
	}

	private static void OnLevelTransitionPrefix()
	{
		if (!((Object)(object)_instance == (Object)null))
		{
			_instance._inLevelTransition = true;
			_instance.ResetRuntimeState("Level transition");
		}
	}

	private void TryPatchTransitions()
	{
		MethodInfo prefix = AccessTools.Method(typeof(DynamicPressurePlugin), "OnLevelTransitionPrefix", (Type[])null, (Type[])null);
		TryPatchMethod(typeof(NextLevelTrigger), "MakeTransition", prefix);
		TryPatchMethod(typeof(GameManager), "CompleteLevel", prefix);
		TryPatchAllNamedMethods(typeof(GameManager), "GoToLevel", prefix);
		TryPatchAllNamedMethods(typeof(GameManager), "GoToChurchHub", prefix);
		TryPatchAllNamedMethods(typeof(GameManager), "GoToCarHub", prefix);
		TryPatchAllNamedMethods(typeof(GameManager), "SwitchLevel", prefix);
	}

	private void TryPatchNpcDie()
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Npc), "Die", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(DynamicPressurePlugin), "OnNpcDiePostfix", (Type[])null, (Type[])null);
			if (methodInfo != null && methodInfo2 != null)
			{
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log("Patched Npc.Die.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Failed to patch Npc.Die: method not found.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch Npc.Die: " + ex.Message));
		}
	}

	private void TryPatchMethod(Type type, string methodName, MethodInfo prefix)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		try
		{
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch skipped: " + type.Name + "." + methodName));
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(prefix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log("Patched " + type.Name + "." + methodName);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch failed: " + type.Name + "." + methodName + " / " + ex.Message));
		}
	}

	private void TryPatchAllNamedMethods(Type type, string methodName, MethodInfo prefix)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Expected O, but got Unknown
		try
		{
			MethodInfo[] array = (from m in AccessTools.GetDeclaredMethods(type)
				where m.Name == methodName
				select m).ToArray();
			if (array.Length == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch skipped: " + type.Name + "." + methodName));
				return;
			}
			for (int i = 0; i < array.Length; i++)
			{
				_harmony.Patch((MethodBase)array[i], new HarmonyMethod(prefix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Log("Patched " + type.Name + "." + methodName + " x" + array.Length);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch failed: " + type.Name + "." + methodName + " / " + ex.Message));
		}
	}

	private void AddEvent(string text)
	{
		_recentEvents.Add(DateTime.Now.ToString("HH:mm:ss") + " " + text);
		while (_recentEvents.Count > 8)
		{
			_recentEvents.RemoveAt(0);
		}
	}

	private void Log(string text)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)text);
	}

	private void OnGUI()
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		if (_enableOverlay.Value)
		{
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(20f, 120f, 620f, 620f);
			GUI.Box(val, "Dynamic Pressure Debug");
			GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 25f, ((Rect)(ref val)).width - 20f, ((Rect)(ref val)).height - 35f));
			GUILayout.Label("Enabled: " + _enableMod.Value + " | AutoSpawn: " + _enableAutoSpawn.Value + " | Style: " + GetStyle(), Array.Empty<GUILayoutOption>());
			GUILayout.Label("GameState: " + _snapshot.gameState + " | SafeZone: " + _snapshot.inSafeZone + " | Env: " + _snapshot.currentEnvironment, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Player: " + (_snapshot.hasPlayer ? "OK" : "Missing") + " | Alive: " + _snapshot.playerAlive + " | HP: " + Mathf.RoundToInt(_snapshot.playerHp * 100f) + "% | DamageAgo: " + _snapshot.playerTimeSinceDamage.ToString("0.0") + "s", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Room: " + _snapshot.playerRoomName + " | EndRoom: " + _snapshot.playerRoomIsEndRoom, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("Pressure", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Original Hostile Alive: " + _snapshot.originalHostileAlive + " | Engaged: " + _snapshot.originalEngagedHostiles + " | Targeting: " + _snapshot.originalTargetingPlayer, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Mod Hostile Alive: " + _snapshot.modHostileAlive + " | Targeting: " + _snapshot.modTargetingPlayer, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Original Pressure: " + _snapshot.originalPressure + " | Mod Pressure: +" + _snapshot.modPressure + " | Current: " + _snapshot.currentPressure + " / Target: " + _snapshot.targetPressure + " | Deficit: " + _snapshot.deficit, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("Spawn Decision", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Last Decision: " + _lastDecision + " | Reason: " + _lastBlockReason, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Next Spawn In: " + _snapshot.nextSpawnIn.ToString("0.0") + "s | Candidate Source: " + _snapshot.candidateSource + " | Candidates: " + _snapshot.candidateCount, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("Mod Impact", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Spawned This Level: " + _spawnedThisLevel + " / " + GetMaxModPerLevel(), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Spawned Since Last Original Kill: " + _spawnedSinceLastOriginalKill, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Time Since Original Kill: " + _snapshot.timeSinceLastOriginalKill.ToString("0.0") + "s | Time Since Mod Kill: " + _snapshot.timeSinceLastModKill.ToString("0.0") + "s", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Would Delay OnAllEnemiesDead: " + _snapshot.wouldDelayOnAllEnemiesDead, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Last Spawn: " + _lastSpawnSummary, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Last Aggro Report: " + _lastAggroReport, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("Keys: F8 Manual Spawn | F9 Toggle Overlay", Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("Recent Events", Array.Empty<GUILayoutOption>());
			for (int i = 0; i < _recentEvents.Count; i++)
			{
				GUILayout.Label(_recentEvents[i], Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndArea();
		}
	}
}
public sealed class DynamicPressureSpawnMarker : MonoBehaviour
{
	public UnitSO sourceUnitSo;

	public float spawnTime;

	public Vector3 spawnPosition;

	public Room spawnRoom;

	public string spawnReason;

	public int waveId;

	public bool aggroReported;

	public float lastAggroReportTime;

	public bool targetingPlayerAfterReport;

	public bool hasKnownPlayerPositionAfterReport;
}
public enum SpawnBlockReason
{
	None,
	Disabled,
	NoGameManager,
	NoPlayer,
	PlayerDead,
	SafeZone,
	GameStateNotRunning,
	LowHealth,
	RecentDamage,
	EndRoomBlocked,
	SpawnRoomIsEndRoom,
	NoOriginalHostiles,
	NoEngagedOriginalHostiles,
	NotEnoughOriginalHostiles,
	NoCandidateUnits,
	NoValidNpcSpawnPoint,
	Cooldown,
	MaxModSpawnedAlive,
	RoomSpawnBudgetExceeded,
	LevelSpawnBudgetExceeded,
	PressureAlreadyHigh,
	SpawnAsyncFailed,
	LevelTransition,
	RecentModKillOnly,
	NoOriginalKillProgress,
	SpawnBudgetPerOriginalExceeded
}