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;
}
}
}