Decompiled source of GravWarden ChaosArena v0.2.1

Mods/ChaosArena.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib.BoneMenu;
using ChaosArena;
using Il2CppCysharp.Threading.Tasks;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSystem;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(ChaosArenaMod), "CHAOS ARENA", "0.2.1", "GravWarden", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("0.0.0.0")]
[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 ChaosArena
{
	public sealed class ChaosArenaMod : MelonMod
	{
		private const int QuestEnemyLimit = 4;

		private const float DefaultSpawnRadius = 4.5f;

		private const float DefaultSpawnInterval = 1.25f;

		private const float SpawnTimeout = 20f;

		private const float Intermission = 5f;

		private const string PeasantSmall = "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleS";

		private const string PeasantMedium = "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleM";

		private const string PeasantLarge = "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleL";

		private const string Cultist = "SLZ.BONELAB.Content.Spawnable.NPCCultist";

		private const string SecurityGuard = "SLZ.BONELAB.Content.Spawnable.NPCSecurityGuard";

		private static readonly string[] StandardEnemies = new string[4] { "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleS", "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleM", "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleL", "SLZ.BONELAB.Content.Spawnable.NPCCultist" };

		private static readonly string[] ChaosModifiers = new string[4] { "SWARM", "CULT RUSH", "ARMORED", "MIXED BAG" };

		private static readonly string[] HealthMemberNames = new string[6] { "curr_Health", "cur_Health", "currentHealth", "currHealth", "health", "e_health" };

		private static readonly BindingFlags InstanceMemberFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private readonly List<Poolee> _enemies = new List<Poolee>();

		private readonly Random _random = new Random();

		private MelonPreferences_Entry<int> _bestWavePreference;

		private MelonPreferences_Entry<int> _difficultyPreference;

		private MelonPreferences_Entry<float> _spawnRadiusPreference;

		private MelonPreferences_Entry<float> _spawnIntervalPreference;

		private MelonPreferences_Entry<bool> _showHudPreference;

		private bool _runActive;

		private int _runGeneration;

		private int _wave;

		private int _waveSize;

		private int _spawnedThisWave;

		private int _pendingSpawns;

		private int _score;

		private int _difficulty = 1;

		private bool _waveCleared;

		private bool _spawnFailed;

		private bool _healthReadWarningLogged;

		private float _nextEnemyCheckTime;

		private GameObject _hudObject;

		private TextMeshProUGUI _hudText;

		private string _lastHudText = string.Empty;

		private float _nextActionTime;

		private float _spawnDeadline;

		private string _currentModifier = string.Empty;

		private string _sceneName = string.Empty;

		private Vector3 _arenaCenter;

		public override void OnInitializeMelon()
		{
			MelonPreferences_Category val = MelonPreferences.CreateCategory("ChaosArena", "CHAOS ARENA");
			_bestWavePreference = val.CreateEntry<int>("BestWave", 0, "Best wave", (string)null, false, false, (ValueValidator)null, (string)null);
			_difficultyPreference = val.CreateEntry<int>("Difficulty", 1, "Difficulty", "0 = Easy, 1 = Standard, 2 = Nightmare.", false, false, (ValueValidator)null, (string)null);
			_spawnRadiusPreference = val.CreateEntry<float>("SpawnRadius", 4.5f, "Spawn distance", (string)null, false, false, (ValueValidator)null, (string)null);
			_spawnIntervalPreference = val.CreateEntry<float>("SpawnInterval", 1.25f, "Spawn interval", (string)null, false, false, (ValueValidator)null, (string)null);
			_showHudPreference = val.CreateEntry<bool>("ShowHud", true, "Show run HUD", (string)null, false, false, (ValueValidator)null, (string)null);
			_difficulty = Mathf.Clamp(_difficultyPreference.Value, 0, 2);
			_spawnRadiusPreference.Value = Mathf.Clamp(_spawnRadiusPreference.Value, 2f, 10f);
			_spawnIntervalPreference.Value = Mathf.Clamp(_spawnIntervalPreference.Value, 0.25f, 5f);
			CreateBoneMenu();
			MelonLogger.Msg("Ready. Start a run from BoneMenu > CHAOS ARENA.");
		}

		private void CreateBoneMenu()
		{
			//IL_000a: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_00d1: 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_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			Page obj = Page.Root.CreatePage("CHAOS ARENA", Color.red, 0, true);
			Page val = obj.CreatePage("Settings", Color.white, 0, true);
			((Element)obj.CreateFunction("Start Run", Color.green, (Action)StartRun)).SetTooltip("Start a wave run around your current position in the active level.");
			((Element)obj.CreateFunction("Stop Run / Clear Enemies", Color.yellow, (Action)StopRun)).SetTooltip("Stop the run and safely despawn its active enemies.");
			obj.CreateFunction("Set Difficulty: Easy", Color.white, (Action)delegate
			{
				SetDifficulty(0);
			});
			obj.CreateFunction("Set Difficulty: Standard", Color.white, (Action)delegate
			{
				SetDifficulty(1);
			});
			obj.CreateFunction("Set Difficulty: Nightmare", Color.white, (Action)delegate
			{
				SetDifficulty(2);
			});
			((Element)obj.CreateFunction("Show Run Status", Color.cyan, (Action)ShowStatus)).SetTooltip("Write your current wave and score to the MelonLoader log.");
			((Element)val.CreateBool("Show Run HUD", Color.white, _showHudPreference.Value, (Action<bool>)delegate(bool value)
			{
				_showHudPreference.Value = value;
				if (!value)
				{
					DestroyHud();
				}
				else if (_runActive)
				{
					Camera val2 = FindGameplayCamera();
					if ((Object)(object)val2 != (Object)null)
					{
						CreateHud(((Component)val2).transform);
					}
				}
				MelonPreferences.Save();
			})).SetTooltip("Show the wave and score display in the headset.");
			((Element)val.CreateFloat("Spawn Distance (m)", Color.white, _spawnRadiusPreference.Value, 0.5f, 2f, 10f, (Action<float>)delegate(float value)
			{
				SaveFloatPreference(_spawnRadiusPreference, value);
			})).SetTooltip("Distance around your start position where enemies appear.");
			((Element)val.CreateFloat("Spawn Delay (s)", Color.white, _spawnIntervalPreference.Value, 0.25f, 0.25f, 5f, (Action<float>)delegate(float value)
			{
				SaveFloatPreference(_spawnIntervalPreference, value);
			})).SetTooltip("Delay between enemy spawn requests.");
		}

		private static void SaveFloatPreference(MelonPreferences_Entry<float> preference, float value)
		{
			preference.Value = value;
			MelonPreferences.Save();
		}

		private void SetDifficulty(int difficulty)
		{
			if (_runActive)
			{
				MelonLogger.Warning("Stop the current run before changing difficulty.");
				return;
			}
			_difficulty = Mathf.Clamp(difficulty, 0, 2);
			_difficultyPreference.Value = _difficulty;
			MelonPreferences.Save();
			MelonLogger.Msg("Difficulty set to " + DifficultyName(_difficulty) + ".");
		}

		private static string DifficultyName(int difficulty)
		{
			return difficulty switch
			{
				0 => "Easy", 
				2 => "Nightmare", 
				_ => "Standard", 
			};
		}

		private void StartRun()
		{
			//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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (_runActive)
			{
				MelonLogger.Warning("A CHAOS ARENA run is already active.");
				return;
			}
			Camera val = FindGameplayCamera();
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Error("Cannot start: no active gameplay camera was found. Load fully into a level and try again.");
				return;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			_sceneName = ((Scene)(ref activeScene)).name;
			if (string.IsNullOrEmpty(_sceneName))
			{
				MelonLogger.Error("Cannot start: the active BONELAB scene is not ready.");
				return;
			}
			_runGeneration++;
			_arenaCenter = ((Component)val).transform.position;
			_runActive = true;
			_wave = 0;
			_score = 0;
			_waveCleared = false;
			_spawnFailed = false;
			_healthReadWarningLogged = false;
			_nextActionTime = Time.time;
			_nextEnemyCheckTime = 0f;
			_spawnDeadline = 0f;
			_enemies.Clear();
			if (_showHudPreference.Value)
			{
				try
				{
					CreateHud(((Component)val).transform);
				}
				catch (Exception ex)
				{
					DestroyHud();
					MelonLogger.Error("The in-headset status display could not be created: " + ex);
				}
			}
			BeginNextWave();
			MelonLogger.Msg("Run started in " + _sceneName + ". Difficulty: " + DifficultyName(_difficulty) + ". Preparing the first enemy now.");
		}

		private void BeginNextWave()
		{
			_wave++;
			_spawnedThisWave = 0;
			_pendingSpawns = 0;
			_waveCleared = false;
			_currentModifier = ChaosModifiers[_random.Next(ChaosModifiers.Length)];
			int num = 1 + _wave;
			if (_difficulty == 0)
			{
				num = 1 + _wave / 2;
			}
			else if (_difficulty == 2)
			{
				num += 2;
			}
			if (_currentModifier == "SWARM")
			{
				num++;
			}
			_waveSize = Mathf.Clamp(num, 1, 4);
			_nextActionTime = Time.time;
			MelonLogger.Msg("WAVE " + _wave + " — " + _currentModifier + " (" + _waveSize + " enemies).");
		}

		public override void OnUpdate()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!_runActive)
			{
				return;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name != _sceneName)
			{
				MelonLogger.Warning("Level changed. Stopping the arena and clearing its enemies.");
				StopRun();
				return;
			}
			if (_spawnFailed)
			{
				_spawnFailed = false;
				StopRun();
				return;
			}
			if (_pendingSpawns > 0 && Time.time >= _spawnDeadline)
			{
				MelonLogger.Error("Enemy spawn did not complete within " + 20f + " seconds. Stopping the run; check that this BONELAB level is fully loaded and the enemy spawnable is available.");
				_spawnFailed = true;
				return;
			}
			if (Time.time >= _nextEnemyCheckTime)
			{
				RemoveInactiveEnemies();
				_nextEnemyCheckTime = Time.time + 0.25f;
			}
			UpdateHud();
			if (_spawnedThisWave < _waveSize && _pendingSpawns == 0 && Time.time >= _nextActionTime)
			{
				SpawnNextEnemy();
				_nextActionTime = Time.time + _spawnIntervalPreference.Value;
			}
			else
			{
				if (_spawnedThisWave != _waveSize || _pendingSpawns != 0 || _enemies.Count != 0)
				{
					return;
				}
				if (!_waveCleared)
				{
					_waveCleared = true;
					_score += _wave * 100;
					if (_wave > _bestWavePreference.Value)
					{
						_bestWavePreference.Value = _wave;
						MelonPreferences.Save();
					}
					_nextActionTime = Time.time + 5f;
					MelonLogger.Msg("WAVE " + _wave + " CLEARED — score " + _score + ". Next wave in 5 seconds.");
				}
				else if (Time.time >= _nextActionTime)
				{
					BeginNextWave();
				}
			}
		}

		private void SpawnNextEnemy()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			string barcode = SelectEnemyBarcode();
			if (!TryFindSpawnPosition(_spawnedThisWave, _waveSize, out var spawnPosition))
			{
				MelonLogger.Error("Could not find a floor spawn point near the arena start. Move to an open area and start again.");
				_spawnFailed = true;
				return;
			}
			Vector3 val = _arenaCenter - spawnPosition;
			val.y = 0f;
			Quaternion val2 = ((((Vector3)(ref val)).sqrMagnitude > 0.01f) ? Quaternion.LookRotation(((Vector3)(ref val)).normalized) : Quaternion.identity);
			int generation = _runGeneration;
			_pendingSpawns++;
			_spawnDeadline = Time.time + 20f;
			_spawnedThisWave++;
			MelonLogger.Msg("Requesting enemy " + _spawnedThisWave + " of " + _waveSize + " for wave " + _wave + " (" + barcode + ").");
			try
			{
				Spawnable val3 = new Spawnable
				{
					crateRef = new SpawnableCrateReference(barcode),
					policyData = null
				};
				AssetSpawner.Register(val3);
				Nullable<Vector3> val4 = new Nullable<Vector3>(Vector3.zero)
				{
					hasValue = false
				};
				Nullable<int> val5 = new Nullable<int>(0)
				{
					hasValue = false
				};
				UniTask<Poolee> val6 = AssetSpawner.SpawnAsync(val3, spawnPosition, val2, val4, (Transform)null, false, val5, (Action<GameObject>)null, (Action<GameObject>)null, (Action<GameObject>)null);
				Awaiter<Poolee> awaiter = val6.GetAwaiter();
				awaiter.OnCompleted((Action)delegate
				{
					if (!_runActive || generation != _runGeneration)
					{
						try
						{
							Poolee result = awaiter.GetResult();
							if ((Object)(object)result != (Object)null)
							{
								result.Despawn();
							}
							return;
						}
						catch (Exception ex2)
						{
							MelonLogger.Warning("A stopped run's pending spawn could not be cleared: " + ex2.Message);
							return;
						}
					}
					_pendingSpawns = Mathf.Max(0, _pendingSpawns - 1);
					_spawnDeadline = 0f;
					Poolee result2;
					try
					{
						result2 = awaiter.GetResult();
					}
					catch (Exception ex3)
					{
						MelonLogger.Error("Enemy spawn failed: " + ex3);
						_spawnFailed = true;
						return;
					}
					if ((Object)(object)result2 == (Object)null)
					{
						MelonLogger.Error("BONELAB could not spawn enemy barcode " + barcode + ". Ending this run instead of counting a failed spawn as a clear.");
						_spawnFailed = true;
					}
					else
					{
						_enemies.Add(result2);
						MelonLogger.Msg("Enemy spawned successfully. Active enemies: " + _enemies.Count + " / " + _waveSize + ".");
					}
				});
			}
			catch (Exception ex)
			{
				_pendingSpawns = Mathf.Max(0, _pendingSpawns - 1);
				MelonLogger.Error("Could not request an enemy spawn: " + ex);
				_spawnFailed = true;
			}
		}

		private string SelectEnemyBarcode()
		{
			if (_currentModifier == "CULT RUSH")
			{
				return "SLZ.BONELAB.Content.Spawnable.NPCCultist";
			}
			if (_currentModifier == "ARMORED")
			{
				return "SLZ.BONELAB.Content.Spawnable.NPCSecurityGuard";
			}
			if (_currentModifier == "MIXED BAG")
			{
				if (_random.Next(2) != 0)
				{
					return "SLZ.BONELAB.Content.Spawnable.NPCSecurityGuard";
				}
				return "SLZ.BONELAB.Content.Spawnable.NPCPeasantMaleS";
			}
			if (_difficulty == 2 && _random.Next(3) == 0)
			{
				return "SLZ.BONELAB.Content.Spawnable.NPCSecurityGuard";
			}
			return StandardEnemies[_random.Next(StandardEnemies.Length)];
		}

		private bool TryFindSpawnPosition(int enemyIndex, int enemyCount, out Vector3 spawnPosition)
		{
			//IL_0057: 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)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00b7: Unknown result type (might be due to invalid IL or missing references)
			int num = 8;
			float num2 = (float)Math.PI * 2f * (float)enemyIndex / (float)Mathf.Max(1, enemyCount) + (float)_wave * 0.37f;
			float value = _spawnRadiusPreference.Value;
			RaycastHit val2 = default(RaycastHit);
			for (int i = 0; i < num; i++)
			{
				float num3 = num2 + (float)Math.PI * 2f * (float)i / (float)num;
				Vector3 val = new Vector3(Mathf.Sin(num3), 0f, Mathf.Cos(num3)) * value;
				if (Physics.Raycast(_arenaCenter + val + Vector3.up * 8f, Vector3.down, ref val2, 24f, -5, (QueryTriggerInteraction)1))
				{
					spawnPosition = ((RaycastHit)(ref val2)).point + Vector3.up * 0.2f;
					return true;
				}
			}
			spawnPosition = Vector3.zero;
			return false;
		}

		private void RemoveInactiveEnemies()
		{
			for (int num = _enemies.Count - 1; num >= 0; num--)
			{
				Poolee val = _enemies[num];
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || !((Component)val).gameObject.activeInHierarchy || IsEnemyDefeated(val))
				{
					_enemies.RemoveAt(num);
				}
			}
		}

		private bool IsEnemyDefeated(Poolee enemy)
		{
			try
			{
				Component component = ((Component)enemy).gameObject.GetComponent("AIBrain");
				Behaviour val = (Behaviour)(object)((component is Behaviour) ? component : null);
				if ((Object)(object)val == (Object)null)
				{
					WarnHealthDetectionUnavailable();
					return false;
				}
				if (!val.enabled)
				{
					return true;
				}
				if (TryReadHealth(val, out var health))
				{
					return health <= 0f;
				}
				if (TryReadHealth(ReadMember(val, "health"), out health))
				{
					return health <= 0f;
				}
				Component component2 = ((Component)enemy).gameObject.GetComponent("Health");
				if ((Object)(object)component2 != (Object)null && TryReadHealth(component2, out health))
				{
					return health <= 0f;
				}
			}
			catch (Exception ex)
			{
				if (!_healthReadWarningLogged)
				{
					_healthReadWarningLogged = true;
					MelonLogger.Warning("Could not inspect NPC health; defeated enemies will be cleared when BONELAB despawns them. " + ex.Message);
				}
				return false;
			}
			WarnHealthDetectionUnavailable();
			return false;
		}

		private static bool TryReadHealth(object target, out float health)
		{
			health = 0f;
			if (target == null)
			{
				return false;
			}
			string[] healthMemberNames = HealthMemberNames;
			foreach (string name in healthMemberNames)
			{
				object obj = ReadMember(target, name);
				if (obj != null)
				{
					if (obj is float)
					{
						health = (float)obj;
						return true;
					}
					if (obj is double)
					{
						health = (float)(double)obj;
						return true;
					}
					if (obj is int)
					{
						health = (int)obj;
						return true;
					}
				}
			}
			return false;
		}

		private static object ReadMember(object target, string name)
		{
			if (target == null)
			{
				return null;
			}
			Type type = target.GetType();
			PropertyInfo property = type.GetProperty(name, InstanceMemberFlags);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				return property.GetValue(target, null);
			}
			FieldInfo field = type.GetField(name, InstanceMemberFlags);
			if (!(field != null))
			{
				return null;
			}
			return field.GetValue(target);
		}

		private void WarnHealthDetectionUnavailable()
		{
			if (!_healthReadWarningLogged)
			{
				_healthReadWarningLogged = true;
				MelonLogger.Warning("This BONELAB NPC did not expose readable health. Its wave slot clears after the NPC is despawned.");
			}
		}

		private void CreateHud(Transform cameraTransform)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_00a2: 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_00c6: 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)
			DestroyHud();
			_hudObject = new GameObject("ChaosArenaHUD");
			Canvas obj = _hudObject.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)2;
			obj.worldCamera = ((Component)cameraTransform).GetComponent<Camera>();
			obj.overrideSorting = true;
			obj.sortingOrder = 100;
			_hudObject.transform.SetParent(cameraTransform, false);
			_hudObject.transform.localPosition = new Vector3(-0.31f, -0.17f, 0.85f);
			_hudObject.transform.localRotation = Quaternion.identity;
			_hudObject.transform.localScale = Vector3.one * 0.0006f;
			((RectTransform)_hudObject.transform).sizeDelta = new Vector2(900f, 300f);
			_hudText = _hudObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_hudText).fontSize = 40f;
			((Graphic)_hudText).color = Color.white;
			((TMP_Text)_hudText).alignment = (TextAlignmentOptions)257;
			((TMP_Text)_hudText).enableWordWrapping = false;
			((TMP_Text)_hudText).richText = false;
			_lastHudText = string.Empty;
			UpdateHud();
		}

		private void UpdateHud()
		{
			if (!((Object)(object)_hudText == (Object)null))
			{
				string text = (_waveCleared ? ("NEXT WAVE IN " + Mathf.Max(0, Mathf.CeilToInt(_nextActionTime - Time.time)) + "s") : ("WAVE " + _wave + "  //  " + _currentModifier));
				string text2 = "CHAOS ARENA\n" + text + "\nENEMIES " + _enemies.Count + " / " + _waveSize + "\nSCORE " + _score + "  |  BEST " + _bestWavePreference.Value;
				if (!(text2 == _lastHudText))
				{
					((TMP_Text)_hudText).text = text2;
					_lastHudText = text2;
				}
			}
		}

		private void DestroyHud()
		{
			if ((Object)(object)_hudObject != (Object)null)
			{
				Object.Destroy((Object)(object)_hudObject);
			}
			_hudObject = null;
			_hudText = null;
			_lastHudText = string.Empty;
		}

		private void ShowStatus()
		{
			if (!_runActive)
			{
				MelonLogger.Msg("No active CHAOS ARENA run. Best wave: " + _bestWavePreference.Value + ".");
				return;
			}
			MelonLogger.Msg("CHAOS ARENA — Wave " + _wave + " (" + _currentModifier + "), score " + _score + ", active enemies " + _enemies.Count + ".");
		}

		private void StopRun()
		{
			if (!_runActive && _enemies.Count == 0 && _pendingSpawns == 0)
			{
				DestroyHud();
				return;
			}
			_runActive = false;
			_runGeneration++;
			_spawnFailed = false;
			for (int num = _enemies.Count - 1; num >= 0; num--)
			{
				Poolee val = _enemies[num];
				if ((Object)(object)val != (Object)null)
				{
					try
					{
						val.Despawn();
					}
					catch (Exception ex)
					{
						MelonLogger.Warning("Could not despawn an arena enemy: " + ex.Message);
					}
				}
			}
			_enemies.Clear();
			_pendingSpawns = 0;
			_spawnDeadline = 0f;
			_nextEnemyCheckTime = 0f;
			DestroyHud();
			MelonLogger.Msg("CHAOS ARENA stopped at wave " + _wave + " with score " + _score + ".");
		}

		public override void OnDeinitializeMelon()
		{
			if (_runActive)
			{
				StopRun();
			}
			else
			{
				DestroyHud();
			}
		}

		private static Camera FindGameplayCamera()
		{
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null && ((Behaviour)main).enabled && ((Component)main).gameObject.activeInHierarchy)
			{
				return main;
			}
			Camera[] array = Il2CppArrayBase<Camera>.op_Implicit(Object.FindObjectsOfType<Camera>());
			Camera val = null;
			foreach (Camera val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && ((Behaviour)val2).enabled && ((Component)val2).gameObject.activeInHierarchy)
				{
					if (((Component)val2).CompareTag("MainCamera"))
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null || val2.depth > val.depth)
					{
						val = val2;
					}
				}
			}
			return val;
		}
	}
}