Decompiled source of EverythingCanDieAlternative v1.1.63
nwnt.EverythingCanDieAlternative.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using EverythingCanDieAlternative.ModCompatibility; using EverythingCanDieAlternative.ModCompatibility.Handlers; using EverythingCanDieAlternative.UI; using GameNetcodeStuff; using HarmonyLib; using LethalNetworkAPI; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("EverythingCanDieAlternative")] [assembly: AssemblyFileVersion("1.1.63")] [assembly: AssemblyDescription("A mod that makes everything in Lethal Company damageable")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.1.63.0")] namespace EverythingCanDieAlternative { public class DespawnConfiguration { private static DespawnConfiguration _instance; private ConfigFile _configFile; private readonly Dictionary<string, bool> _enemyDespawnEnabled = new Dictionary<string, bool>(); private readonly string[] _enemiesWithProperDeathAnimations = new string[22] { "DOGDAY", "DOGDAYCRITTER", "PICKYCRITTER", "CATNAPCRITTER", "BOBBYCRITTER", "CRAFTYCRITTER", "HOPPYCRITTER", "BUBBACRITTER", "KICKINCRITTER", "BABOONHAWK", "MANEATER", "CENTIPEDE", "CRAWLER", "TULIPSNAKE", "FLOWERMAN", "FORESTGIANT", "HOARDINGBUG", "MASKED", "MOUTHDOG", "NUTCRACKER", "BUNKERSPIDER", "BUSHWOLF" }; public static DespawnConfiguration Instance => _instance ?? (_instance = new DespawnConfiguration()); public ConfigEntry<bool> EnableDespawnFeature { get; private set; } private DespawnConfiguration() { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "nwnt.EverythingCanDieAlternative_Despawn_Rules.cfg"); _configFile = new ConfigFile(text, true); EnableDespawnFeature = _configFile.Bind<bool>("General", "EnableDespawnFeature", true, new ConfigDescription("If true, dead enemies can despawn based on other settings", (AcceptableValueBase)null, Array.Empty<object>())); PreCreateEnemyConfigEntries(); } public void ReloadConfig() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown try { _configFile.Reload(); _enemyDespawnEnabled.Clear(); EnableDespawnFeature = _configFile.Bind<bool>("General", "EnableDespawnFeature", true, new ConfigDescription("If true, dead enemies can despawn based on other settings", (AcceptableValueBase)null, Array.Empty<object>())); Plugin.LogInfo("Despawn configuration reloaded from disk"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error reloading despawn config: " + ex.Message)); } } private void PreCreateEnemyConfigEntries() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown if (Plugin.enemies == null || Plugin.enemies.Count <= 0) { return; } foreach (EnemyType enemy in Plugin.enemies) { string text = Plugin.RemoveInvalidCharacters(enemy.enemyName).ToUpper(); bool flag = !_enemiesWithProperDeathAnimations.Contains(text); _configFile.Bind<bool>("Enemies", text + ".Despawn", flag, new ConfigDescription("If true, " + enemy.enemyName + " will despawn after death", (AcceptableValueBase)null, Array.Empty<object>())); } } public bool ShouldDespawnEnemy(string enemyName) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown if (!EnableDespawnFeature.Value) { return false; } string text = Plugin.RemoveInvalidCharacters(enemyName).ToUpper(); if (_enemyDespawnEnabled.TryGetValue(text, out var value)) { return value; } bool flag = true; string[] enemiesWithProperDeathAnimations = _enemiesWithProperDeathAnimations; foreach (string text2 in enemiesWithProperDeathAnimations) { if (text == text2) { flag = false; break; } } ConfigEntry<bool> val = _configFile.Bind<bool>("Enemies", text + ".Despawn", flag, new ConfigDescription("If true, " + enemyName + " will despawn after death", (AcceptableValueBase)null, Array.Empty<object>())); _enemyDespawnEnabled[text] = val.Value; return val.Value; } public void ClearCache() { _enemyDespawnEnabled.Clear(); Plugin.Log.LogInfo((object)"Despawn cache cleared"); } } public class EnemyControlConfiguration { private static EnemyControlConfiguration _instance; private ConfigFile _configFile; private readonly Dictionary<string, bool> _enemyModEnabled = new Dictionary<string, bool>(); private bool _configEntriesCreated = false; public static EnemyControlConfiguration Instance => _instance ?? (_instance = new EnemyControlConfiguration()); private EnemyControlConfiguration() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "nwnt.EverythingCanDieAlternative_Enemy_Control.cfg"); _configFile = new ConfigFile(text, true); _configFile.Bind<string>("Enemies", "_INFO", "Control which enemies are affected by this mod", new ConfigDescription("This is an Experimental Feature: If you set an enemy's value to 'false', it will not be affected by the EverythingCanDieAlternative mod. The health and hit synconization, and the despawn feature will not take effect for the configured enemy. This can be useful if specific enemies have built-in hit/health/death mechanisms that you want to preserve, but that ECDA overwrites. Now you can preserve them by setting the enemy to false.", (AcceptableValueBase)null, Array.Empty<object>())); } public void ReloadConfig() { try { _configFile.Reload(); _enemyModEnabled.Clear(); Plugin.LogInfo("Enemy control configuration reloaded from disk"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error reloading enemy control config: " + ex.Message)); } } public void PreCreateEnemyConfigEntries() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown if (_configEntriesCreated || Plugin.enemies == null || Plugin.enemies.Count <= 0) { return; } Plugin.LogInfo($"Creating control entries for {Plugin.enemies.Count} enemy types"); foreach (EnemyType enemy in Plugin.enemies) { if (!((Object)(object)enemy == (Object)null) && !string.IsNullOrEmpty(enemy.enemyName)) { string text = Plugin.RemoveInvalidCharacters(enemy.enemyName).ToUpper(); _configFile.Bind<bool>("Enemies", text + ".Enabled", true, new ConfigDescription("If set to false, " + enemy.enemyName + " will not be affected by this mod", (AcceptableValueBase)null, Array.Empty<object>())); } } _configEntriesCreated = true; } public bool IsModEnabledForEnemy(string enemyName) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown string text = Plugin.RemoveInvalidCharacters(enemyName).ToUpper(); if (_enemyModEnabled.TryGetValue(text, out var value)) { return value; } try { ConfigEntry<bool> val = _configFile.Bind<bool>("Enemies", text + ".Enabled", true, new ConfigDescription("If set to false, " + enemyName + " will not be affected by this mod", (AcceptableValueBase)null, Array.Empty<object>())); _enemyModEnabled[text] = val.Value; return val.Value; } catch (Exception ex) { Plugin.Log.LogError((object)("Error checking if mod is enabled for " + enemyName + ": " + ex.Message + " - defaulting to true")); return true; } } public void ClearCache() { _enemyModEnabled.Clear(); Plugin.LogInfo("Enemy control cache cleared"); } } public static class HealthManager { [Serializable] public struct HitData { public int EnemyInstanceId; public ulong EnemyNetworkId; public int EnemyIndex; public string EnemyName; public float Damage; public ulong PlayerClientId; public override string ToString() { return $"HitData(EnemyId={EnemyInstanceId}, NetworkId={EnemyNetworkId}, Index={EnemyIndex}, Name={EnemyName}, Damage={Damage}, PlayerClientId={PlayerClientId})"; } } [CompilerGenerated] private sealed class <WaitForDeathAnimationAndDespawn>d__29 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public EnemyAI enemy; private int <instanceId>5__1; private int <enemyIndex>5__2; private float <waitTime>5__3; private SellBodiesCompatibility <sellBodiesHandler>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForDeathAnimationAndDespawn>d__29(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <sellBodiesHandler>5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if ((Object)(object)enemy == (Object)null) { return false; } <instanceId>5__1 = ((Object)enemy).GetInstanceID(); <enemyIndex>5__2 = enemy.thisEnemyIndex; <waitTime>5__3 = 0.5f; <sellBodiesHandler>5__4 = ModCompatibilityManager.Instance.GetHandler<SellBodiesCompatibility>("Entity378.sellbodies"); if (<sellBodiesHandler>5__4 != null && <sellBodiesHandler>5__4.IsInstalled) { <waitTime>5__3 = <sellBodiesHandler>5__4.GetDespawnDelay(); Plugin.LogInfo($"Using SellBodies compatibility despawn delay: {<waitTime>5__3}s for {enemy.enemyType.enemyName}"); } <>2__current = (object)new WaitForSeconds(<waitTime>5__3); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)enemy != (Object)null && enemy.isEnemyDead) { if (despawnMessage != null) { despawnMessage.SendClients(<enemyIndex>5__2); } Object.Destroy((Object)(object)((Component)enemy).gameObject); } if (enemiesInDespawnProcess.ContainsKey(<instanceId>5__1)) { enemiesInDespawnProcess.Remove(<instanceId>5__1); } 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(); } } private static int networkVarCounter = 0; private static readonly Dictionary<int, LNetworkVariable<float>> enemyHealthVars = new Dictionary<int, LNetworkVariable<float>>(); private static readonly Dictionary<int, float> enemyMaxHealth = new Dictionary<int, float>(); private static readonly Dictionary<int, bool> processedEnemies = new Dictionary<int, bool>(); private static readonly Dictionary<int, ulong> enemyNetworkIds = new Dictionary<int, ulong>(); private static readonly Dictionary<int, string> enemyNetworkVarNames = new Dictionary<int, string>(); private static readonly Dictionary<int, bool> enemiesInDespawnProcess = new Dictionary<int, bool>(); private static readonly Dictionary<int, bool> immortalEnemies = new Dictionary<int, bool>(); private static readonly Dictionary<int, EnemyAI> enemyInstanceCache = new Dictionary<int, EnemyAI>(); private static readonly Dictionary<int, float> pendingDamage = new Dictionary<int, float>(); private static float lastBatchTime = 0f; private const float BATCH_INTERVAL = 0.1f; private static LNetworkMessage<int> despawnMessage; private static LNetworkMessage<HitData> hitMessage; private static bool networkMessagesCreated = false; public static void Initialize() { enemyHealthVars.Clear(); enemyMaxHealth.Clear(); processedEnemies.Clear(); enemyNetworkIds.Clear(); enemyNetworkVarNames.Clear(); enemiesInDespawnProcess.Clear(); immortalEnemies.Clear(); enemyInstanceCache.Clear(); pendingDamage.Clear(); lastBatchTime = 0f; CreateNetworkMessages(); Plugin.LogInfo("Networked Health Manager initialized"); } private static void CreateNetworkMessages() { try { hitMessage = LNetworkMessage<HitData>.Create("ECDA_HitMessage", (Action<HitData, ulong>)delegate(HitData hitData, ulong clientId) { Plugin.LogInfo($"[HOST] Received hit message from client {clientId}: {hitData}"); if (((NetworkBehaviour)StartOfRound.Instance).IsHost) { EnemyAI val2 = FindEnemyMultiMethod(hitData); PlayerControllerB playerWhoHit = null; if (hitData.PlayerClientId != 0) { PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val3 in allPlayerScripts) { if (val3.actualClientId == hitData.PlayerClientId) { playerWhoHit = val3; break; } } } if ((Object)(object)val2 != (Object)null && !val2.isEnemyDead) { ProcessDamageDirectly(val2, hitData.Damage, playerWhoHit); } else { Plugin.Log.LogWarning((object)$"Could not find enemy: {hitData.EnemyName} (NetworkID: {hitData.EnemyNetworkId}, Index: {hitData.EnemyIndex})"); } } }, (Action<HitData>)null, (Action<HitData, ulong>)null); despawnMessage = LNetworkMessage<int>.Create("ECDA_DespawnMessage", (Action<int, ulong>)delegate(int enemyIndex, ulong clientId) { if (!((NetworkBehaviour)StartOfRound.Instance).IsHost) { EnemyAI val = FindEnemyByIndex(enemyIndex); if ((Object)(object)val != (Object)null) { Plugin.LogInfo($"[CLIENT] Received despawn message for enemy index {enemyIndex}"); Object.Destroy((Object)(object)((Component)val).gameObject); } } }, (Action<int>)null, (Action<int, ulong>)null); Plugin.LogInfo("Network messages created successfully"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Error creating network messages: {arg}"); } } private static EnemyAI FindEnemyByIndex(int index) { EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>(); foreach (EnemyAI val in array) { if (val.thisEnemyIndex == index) { return val; } } return null; } private static EnemyAI FindEnemyMultiMethod(HitData hitData) { if (hitData.EnemyIndex >= 0) { foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies) { if (spawnedEnemy.thisEnemyIndex == hitData.EnemyIndex) { Plugin.LogInfo($"Found enemy by index: {hitData.EnemyIndex}"); return spawnedEnemy; } } } if (hitData.EnemyNetworkId != 0) { EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>(); foreach (EnemyAI val in array) { if (((NetworkBehaviour)val).NetworkObjectId == hitData.EnemyNetworkId) { Plugin.LogInfo($"Found enemy by NetworkObjectId: {hitData.EnemyNetworkId}"); return val; } } } if (!string.IsNullOrEmpty(hitData.EnemyName)) { EnemyAI[] array2 = Object.FindObjectsOfType<EnemyAI>(); foreach (EnemyAI val2 in array2) { if (val2.enemyType.enemyName == hitData.EnemyName) { Plugin.LogInfo("Found enemy by name: " + hitData.EnemyName); return val2; } } } Plugin.Log.LogWarning((object)"Could not find enemy. All enemies in scene:"); EnemyAI[] array3 = Object.FindObjectsOfType<EnemyAI>(); foreach (EnemyAI val3 in array3) { Plugin.Log.LogWarning((object)$" - {val3.enemyType.enemyName}, Index: {val3.thisEnemyIndex}, NetworkId: {((NetworkBehaviour)val3).NetworkObjectId}"); } return null; } public static void SetupEnemy(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null || (Object)(object)enemy.enemyType == (Object)null) { return; } try { int instanceId = ((Object)enemy).GetInstanceID(); enemyInstanceCache[instanceId] = enemy; if ((Object)(object)((NetworkBehaviour)enemy).NetworkObject != (Object)null) { enemyNetworkIds[instanceId] = ((NetworkBehaviour)enemy).NetworkObjectId; } if (processedEnemies.ContainsKey(instanceId) && processedEnemies[instanceId]) { Plugin.LogInfo($"Enemy {enemy.enemyType.enemyName} (ID: {instanceId}) already processed, skipping setup"); return; } string enemyName = enemy.enemyType.enemyName; string mobName = Plugin.RemoveInvalidCharacters(enemyName).ToUpper(); if (!Plugin.IsModEnabledForEnemy(mobName)) { Plugin.LogInfo("Mod disabled for enemy " + enemyName + " via config, using vanilla behavior"); processedEnemies[instanceId] = true; } else if (Plugin.CanMob(".Unimmortal", mobName)) { float num = Plugin.GetMobHealth(mobName, enemy.enemyHP); BrutalCompanyMinusCompatibility handler = ModCompatibilityManager.Instance.GetHandler<BrutalCompanyMinusCompatibility>("SoftDiamond.BrutalCompanyMinusExtraReborn"); if (handler != null && handler.IsInstalled) { num = handler.ApplyBonusHp(num); } string text = $"ECDA_Health_{enemy.thisEnemyIndex}_{networkVarCounter++}"; enemyNetworkVarNames[instanceId] = text; Plugin.LogInfo($"Creating network variable {text} for enemy {enemyName} (ID: {instanceId})"); if (!enemyHealthVars.ContainsKey(instanceId)) { try { LNetworkVariable<float> val = LNetworkVariable<float>.Create(text, num, (LNetworkVariableWritePerms)0, (Action<float, float>)null); val.OnValueChanged += delegate(float oldHealth, float newHealth) { HandleHealthChange(instanceId, newHealth); }; enemyHealthVars[instanceId] = val; } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to create network variable " + text + ": " + ex.Message)); text = $"ECDA_Health_{enemy.thisEnemyIndex}_{networkVarCounter++}_Retry"; Plugin.LogInfo("Retrying with new variable name: " + text); enemyNetworkVarNames[instanceId] = text; LNetworkVariable<float> val = LNetworkVariable<float>.Create(text, num, (LNetworkVariableWritePerms)0, (Action<float, float>)null); val.OnValueChanged += delegate(float oldHealth, float newHealth) { HandleHealthChange(instanceId, newHealth); }; enemyHealthVars[instanceId] = val; } } else { LNetworkVariable<float> val = enemyHealthVars[instanceId]; Plugin.LogInfo($"Using existing health variable for enemy {enemyName} (ID: {instanceId})"); } enemyMaxHealth[instanceId] = num; enemy.enemyType.canDie = true; enemy.enemyType.canBeDestroyed = true; enemy.enemyHP = 999; processedEnemies[instanceId] = true; immortalEnemies[instanceId] = false; Plugin.LogInfo($"Setup enemy {enemyName} (ID: {instanceId}, NetID: {((NetworkBehaviour)enemy).NetworkObjectId}, Index: {enemy.thisEnemyIndex}) with {num} networked health"); } else { Plugin.LogInfo("Enemy " + enemyName + " is configured as immortal (Unimmortal=false, Enabled=true)"); enemy.enemyHP = 999; if (Plugin.ProtectImmortalEnemiesFromInstaKill.Value) { enemy.enemyType.canDie = false; Plugin.LogInfo("Protected immortal enemy " + enemyName + " from insta-kill effects (canDie = false)"); } else { enemy.enemyType.canDie = true; Plugin.LogInfo("Immortal enemy " + enemyName + " can still be killed by insta-kill effects (canDie = true)"); } immortalEnemies[instanceId] = true; processedEnemies[instanceId] = true; Plugin.LogInfo($"Set enemy {enemyName} (ID: {instanceId}) to be immortal with 999 HP"); } } catch (Exception ex2) { Plugin.Log.LogError((object)("Error setting up enemy: " + ex2.Message)); Plugin.Log.LogError((object)("Stack trace: " + ex2.StackTrace)); } } private static void HandleHealthChange(int instanceId, float newHealth) { EnemyAI val = FindEnemyById(instanceId); if ((Object)(object)val == (Object)null) { return; } Plugin.LogInfo($"Health changed for enemy {val.enemyType.enemyName} (ID: {instanceId}): new health = {newHealth}"); if (newHealth <= 0f && !val.isEnemyDead && ((NetworkBehaviour)StartOfRound.Instance).IsHost) { HitmarkerCompatibility handler = ModCompatibilityManager.Instance.GetHandler<HitmarkerCompatibility>("com.github.zehsteam.Hitmarker"); if (handler != null && handler.IsInstalled) { PlayerControllerB lastDamageSource = handler.GetLastDamageSource(instanceId); handler.NotifyEnemyKilled(val, lastDamageSource); } KillEnemy(val); } } private static EnemyAI FindEnemyById(int instanceId) { if (enemyInstanceCache.TryGetValue(instanceId, out var value)) { if ((Object)(object)value != (Object)null) { return value; } enemyInstanceCache.Remove(instanceId); } EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>(); EnemyAI[] array2 = array; foreach (EnemyAI val in array2) { if (((Object)val).GetInstanceID() == instanceId) { enemyInstanceCache[instanceId] = val; return val; } } return null; } public static void ProcessHit(EnemyAI enemy, float damage, PlayerControllerB playerWhoHit) { if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead) { return; } int instanceID = ((Object)enemy).GetInstanceID(); string mobName = Plugin.RemoveInvalidCharacters(enemy.enemyType.enemyName).ToUpper(); if (!Plugin.IsModEnabledForEnemy(mobName)) { Plugin.LogInfo("Mod disabled for enemy " + enemy.enemyType.enemyName + ", not processing hit"); return; } if (immortalEnemies.TryGetValue(instanceID, out var value) && value) { enemy.enemyHP = 999; Plugin.LogInfo("Refreshed immortal enemy " + enemy.enemyType.enemyName + " HP to 999"); return; } LethalHandsCompatibility handler = ModCompatibilityManager.Instance.GetHandler<LethalHandsCompatibility>("SlapitNow.LethalHands"); if (handler != null && handler.IsInstalled && damage == -22f) { damage = handler.ConvertPunchForceToDamage(damage); Plugin.LogInfo($"Converted LethalHands punch to damage: {damage}"); } else if (damage < 0f) { Plugin.Log.LogWarning((object)$"Received negative damage value: {damage}, setting to 0"); damage = 0f; } if (damage <= 0f) { return; } if (((NetworkBehaviour)StartOfRound.Instance).IsHost) { Plugin.LogInfo($"Processing hit locally as host: Enemy {enemy.enemyType.enemyName}, Damage {damage}"); ProcessDamageDirectly(enemy, damage, playerWhoHit); return; } if (!pendingDamage.ContainsKey(instanceID)) { pendingDamage[instanceID] = 0f; } pendingDamage[instanceID] += damage; HitmarkerCompatibility handler2 = ModCompatibilityManager.Instance.GetHandler<HitmarkerCompatibility>("com.github.zehsteam.Hitmarker"); if (handler2 != null && handler2.IsInstalled && (Object)(object)playerWhoHit != (Object)null) { handler2.TrackDamageSource(instanceID, playerWhoHit); } if (Time.time - lastBatchTime >= 0.1f) { SendDamageBatch(); } } private static void SendDamageBatch() { if (pendingDamage.Count == 0) { return; } foreach (KeyValuePair<int, float> item in pendingDamage) { EnemyAI val = FindEnemyById(item.Key); if (!((Object)(object)val != (Object)null) || val.isEnemyDead) { continue; } HitData hitData = default(HitData); hitData.EnemyInstanceId = item.Key; hitData.EnemyNetworkId = ((NetworkBehaviour)val).NetworkObjectId; hitData.EnemyIndex = val.thisEnemyIndex; hitData.EnemyName = val.enemyType.enemyName; hitData.Damage = item.Value; hitData.PlayerClientId = StartOfRound.Instance.localPlayerController?.actualClientId ?? 0; HitData hitData2 = hitData; try { if (hitMessage == null) { Plugin.LogWarning("Hit message is null, recreating it"); CreateNetworkMessages(); } hitMessage.SendServer(hitData2); Plugin.LogInfo($"Sent batched damage: {item.Value} to {val.enemyType.enemyName}"); } catch (Exception arg) { Plugin.LogError($"Error sending batched hit message: {arg}"); } } pendingDamage.Clear(); lastBatchTime = Time.time; } private static void ProcessDamageDirectly(EnemyAI enemy, float damage, PlayerControllerB playerWhoHit = null) { if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead) { return; } int instanceID = ((Object)enemy).GetInstanceID(); string mobName = Plugin.RemoveInvalidCharacters(enemy.enemyType.enemyName).ToUpper(); if (!Plugin.IsModEnabledForEnemy(mobName)) { Plugin.LogInfo("Mod disabled for enemy " + enemy.enemyType.enemyName + ", not processing damage"); return; } if ((Object)(object)playerWhoHit != (Object)null) { HitmarkerCompatibility handler = ModCompatibilityManager.Instance.GetHandler<HitmarkerCompatibility>("com.github.zehsteam.Hitmarker"); if (handler != null && handler.IsInstalled) { handler.TrackDamageSource(instanceID, playerWhoHit); } } if (immortalEnemies.TryGetValue(instanceID, out var value) && value) { enemy.enemyHP = 999; Plugin.LogInfo("Refreshed immortal enemy " + enemy.enemyType.enemyName + " HP to 999"); return; } if (!processedEnemies.ContainsKey(instanceID) || !processedEnemies[instanceID]) { SetupEnemy(enemy); } if (enemyHealthVars.TryGetValue(instanceID, out var value2)) { float value3 = value2.Value; float num = Mathf.Max(0f, value3 - damage); Plugin.LogInfo($"Enemy {enemy.enemyType.enemyName} damaged for {damage}: {value3} -> {num}"); value2.Value = num; } else { Plugin.Log.LogWarning((object)$"No health variable found for enemy {enemy.enemyType.enemyName} (ID: {instanceID})"); } } public static void NotifyClientsOfDestroy(int enemyIndex) { if (despawnMessage != null) { despawnMessage.SendClients(enemyIndex); } } private static void KillEnemy(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead) { return; } Plugin.LogInfo("Killing enemy " + enemy.enemyType.enemyName); SellBodiesCompatibility handler = ModCompatibilityManager.Instance.GetHandler<SellBodiesCompatibility>("Entity378.sellbodies"); if (handler != null && handler.IsInstalled && handler.IsProblemEnemy(enemy.enemyType.enemyName)) { handler.HandleProblemEnemyDeath(enemy); } if (!((NetworkBehaviour)enemy).IsOwner) { Plugin.LogInfo("Attempting to take ownership of " + enemy.enemyType.enemyName + " to kill it"); ulong actualClientId = StartOfRound.Instance.allPlayerScripts[0].actualClientId; enemy.ChangeOwnershipOfEnemy(actualClientId); } LastResortKillerCompatibility handler2 = ModCompatibilityManager.Instance.GetHandler<LastResortKillerCompatibility>("nwnt.EverythingCanDieAlternative.LastResortKiller"); if (handler2 != null) { bool allowDespawn = DespawnConfiguration.Instance.ShouldDespawnEnemy(enemy.enemyType.enemyName); handler2.AttemptToKillEnemy(enemy, allowDespawn); } else { enemy.KillEnemyOnOwnerClient(false); if (enemy.enemyType.enemyName.Contains("Spring")) { Plugin.LogInfo("Using fallback kill method for " + enemy.enemyType.enemyName); enemy.KillEnemyOnOwnerClient(true); } } if (DespawnConfiguration.Instance.ShouldDespawnEnemy(enemy.enemyType.enemyName)) { StartDespawnProcess(enemy); } } private static void StartDespawnProcess(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null) { return; } int instanceID = ((Object)enemy).GetInstanceID(); if (!enemiesInDespawnProcess.ContainsKey(instanceID) || !enemiesInDespawnProcess[instanceID]) { enemiesInDespawnProcess[instanceID] = true; Plugin.LogInfo($"Starting despawn process for {enemy.enemyType.enemyName} (Index: {enemy.thisEnemyIndex})"); if ((Object)(object)StartOfRound.Instance != (Object)null) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(WaitForDeathAnimationAndDespawn(enemy)); } } } [IteratorStateMachine(typeof(<WaitForDeathAnimationAndDespawn>d__29))] private static IEnumerator WaitForDeathAnimationAndDespawn(EnemyAI enemy) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForDeathAnimationAndDespawn>d__29(0) { enemy = enemy }; } public static float GetEnemyHealth(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null) { return 0f; } int instanceID = ((Object)enemy).GetInstanceID(); if (enemyHealthVars.TryGetValue(instanceID, out var value)) { return value.Value; } return 0f; } public static float GetEnemyMaxHealth(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null) { return 0f; } int instanceID = ((Object)enemy).GetInstanceID(); if (enemyMaxHealth.TryGetValue(instanceID, out var value)) { return value; } return 0f; } public static bool IsEnemyTracked(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null) { return false; } int instanceID = ((Object)enemy).GetInstanceID(); return enemyHealthVars.ContainsKey(instanceID) || immortalEnemies.ContainsKey(instanceID); } public static void CleanupExternallyKilledEnemy(EnemyAI enemy) { if (!((Object)(object)enemy == (Object)null)) { int instanceID = ((Object)enemy).GetInstanceID(); if (enemyHealthVars.ContainsKey(instanceID)) { enemyHealthVars.Remove(instanceID); } if (enemyMaxHealth.ContainsKey(instanceID)) { enemyMaxHealth.Remove(instanceID); } if (processedEnemies.ContainsKey(instanceID)) { processedEnemies.Remove(instanceID); } if (enemyNetworkIds.ContainsKey(instanceID)) { enemyNetworkIds.Remove(instanceID); } if (enemyNetworkVarNames.ContainsKey(instanceID)) { enemyNetworkVarNames.Remove(instanceID); } if (immortalEnemies.ContainsKey(instanceID)) { immortalEnemies.Remove(instanceID); } if (enemyInstanceCache.ContainsKey(instanceID)) { enemyInstanceCache.Remove(instanceID); } Plugin.LogInfo("Cleaned up tracking data for externally killed enemy " + enemy.enemyType.enemyName); } } } public static class Patches { [CompilerGenerated] private sealed class <HandleSpikeKillCleanup>d__7 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public EnemyAI enemy; private string <enemyName>5__1; private float <timeout>5__2; private int <instanceId>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <HandleSpikeKillCleanup>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <enemyName>5__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if ((Object)(object)enemy == (Object)null) { return false; } <enemyName>5__1 = enemy.enemyType.enemyName; <timeout>5__2 = 2f; goto IL_008e; case 1: <>1__state = -1; goto IL_008e; case 2: { <>1__state = -1; if ((Object)(object)enemy != (Object)null && enemy.isEnemyDead) { Plugin.LogInfo("Despawning spike-killed enemy: " + <enemyName>5__1); if (enemy.thisEnemyIndex >= 0) { HealthManager.NotifyClientsOfDestroy(enemy.thisEnemyIndex); } Object.Destroy((Object)(object)((Component)enemy).gameObject); } break; } IL_008e: if (!enemy.isEnemyDead && <timeout>5__2 > 0f) { <timeout>5__2 -= Time.deltaTime; <>2__current = null; <>1__state = 1; return true; } if (enemy.isEnemyDead) { Plugin.LogInfo("Cleaning up spike-killed enemy: " + <enemyName>5__1); HealthManager.CleanupExternallyKilledEnemy(enemy); <instanceId>5__3 = ((Object)enemy).GetInstanceID(); HandleKillCompatibility(enemy, <instanceId>5__3, <enemyName>5__1); if (DespawnConfiguration.Instance.ShouldDespawnEnemy(<enemyName>5__1)) { <>2__current = (object)new WaitForSeconds(GetDespawnDelay()); <>1__state = 2; return true; } } break; } 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 static void Initialize(Harmony harmony) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(typeof(StartOfRound), "Start", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogError((object)"Could not find StartOfRound.Start method - patches failed!"); return; } MethodInfo methodInfo2 = AccessTools.Method(typeof(Patches), "StartOfRoundPostfix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo3 = AccessTools.Method(typeof(EnemyAI), "Start", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(Patches), "EnemyAIStartPostfix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo5 = AccessTools.Method(typeof(EnemyAI), "HitEnemyOnLocalClient", (Type[])null, (Type[])null); MethodInfo methodInfo6 = AccessTools.Method(typeof(Patches), "HitEnemyOnLocalClientPrefix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo7 = AccessTools.Method(typeof(EnemyAI), "HitEnemy", (Type[])null, (Type[])null); MethodInfo methodInfo8 = AccessTools.Method(typeof(Patches), "HitEnemyPrefix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo7, new HarmonyMethod(methodInfo8), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo9 = AccessTools.Method(typeof(RoundManager), "FinishGeneratingLevel", (Type[])null, (Type[])null); MethodInfo methodInfo10 = AccessTools.Method(typeof(Patches), "FinishGeneratingLevelPostfix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, new HarmonyMethod(methodInfo10), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo11 = AccessTools.Method(typeof(StartOfRound), "ShipLeave", (Type[])null, (Type[])null); MethodInfo methodInfo12 = AccessTools.Method(typeof(Patches), "ShipLeavePostfix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo11, (HarmonyMethod)null, new HarmonyMethod(methodInfo12), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo13 = AccessTools.Method(typeof(SpikeRoofTrap), "OnTriggerStay", (Type[])null, (Type[])null); MethodInfo methodInfo14 = AccessTools.Method(typeof(Patches), "SpikeRoofTrapPrefix", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo13, new HarmonyMethod(methodInfo14), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"SpikeRoofTrap.OnTriggerStay patched successfully"); Plugin.Log.LogInfo((object)"All Harmony patches applied successfully"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error applying Harmony patches: " + ex.Message)); Plugin.Log.LogError((object)("Stack trace: " + ex.StackTrace)); } } public static void StartOfRoundPostfix(StartOfRound __instance) { try { Plugin.Log.LogInfo((object)"Game starting, initializing networked enemy health system..."); ((BaseUnityPlugin)Plugin.Instance).Config.Reload(); EnemyControlConfiguration.Instance.ReloadConfig(); DespawnConfiguration.Instance.ReloadConfig(); Plugin.LogInfo("All configurations reloaded from files"); HealthManager.Initialize(); Plugin.enemies = new List<EnemyType>(Resources.FindObjectsOfTypeAll<EnemyType>()); Plugin.LogInfo($"Found {Plugin.enemies.Count} enemy types"); foreach (EnemyType enemy in Plugin.enemies) { if ((Object)(object)enemy == (Object)null || string.IsNullOrEmpty(enemy.enemyName)) { Plugin.Log.LogWarning((object)"Found null or invalid enemy type, skipping"); continue; } string mobName = Plugin.RemoveInvalidCharacters(enemy.enemyName).ToUpper(); Plugin.LogInfo("Processing enemy type: " + enemy.enemyName); Plugin.CanMob(".Unimmortal", mobName); float num = 3f; if ((Object)(object)enemy.enemyPrefab != (Object)null) { EnemyAI componentInChildren = enemy.enemyPrefab.GetComponentInChildren<EnemyAI>(); if ((Object)(object)componentInChildren != (Object)null) { if (componentInChildren.enemyHP <= 0) { num = 1f; Plugin.LogInfo("Detected 0 or negative HP for " + enemy.enemyName + ", setting default to 1"); } else { num = Math.Min(componentInChildren.enemyHP, 30f); if (componentInChildren.enemyHP > 30) { Plugin.LogInfo($"Capped HP for {enemy.enemyName} from {componentInChildren.enemyHP} to {num}"); } } } } Plugin.GetMobHealth(mobName, num); Plugin.ShouldDespawn(mobName); } EnemyControlConfiguration.Instance.PreCreateEnemyConfigEntries(); ProcessExistingEnemies(); Plugin.Log.LogInfo((object)"StartOfRoundPostfix completed successfully"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error in StartOfRoundPostfix: " + ex.Message)); Plugin.Log.LogError((object)("Stack trace: " + ex.StackTrace)); } } private static void ProcessExistingEnemies() { try { EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>(); Plugin.LogInfo($"Found {array.Length} active enemies"); EnemyAI[] array2 = array; foreach (EnemyAI val in array2) { if (!((Object)(object)val?.enemyType == (Object)null)) { Plugin.LogInfo("Setting up enemy: " + val.enemyType.enemyName); HealthManager.SetupEnemy(val); } } } catch (Exception ex) { Plugin.Log.LogError((object)("Error processing existing enemies: " + ex.Message)); Plugin.Log.LogError((object)("Stack trace: " + ex.StackTrace)); } } public static void EnemyAIStartPostfix(EnemyAI __instance) { try { if (!((Object)(object)__instance?.enemyType == (Object)null)) { HealthManager.SetupEnemy(__instance); } } catch (Exception ex) { Plugin.Log.LogError((object)("Error in EnemyAIStartPostfix: " + ex.Message)); } } public static bool HitEnemyOnLocalClientPrefix(EnemyAI __instance, int force, Vector3 hitDirection, PlayerControllerB playerWhoHit, bool playHitSFX, int hitID) { try { if ((Object)(object)__instance == (Object)null || __instance.isEnemyDead) { return true; } Plugin.LogInfo(string.Format("Local hit detected on {0} from {1} with force {2}", __instance.enemyType.enemyName, playerWhoHit?.playerUsername ?? "unknown", force)); LethalHandsCompatibility handler = ModCompatibilityManager.Instance.GetHandler<LethalHandsCompatibility>("SlapitNow.LethalHands"); bool flag = handler != null && handler.IsInstalled && force == -22; if (flag) { Plugin.LogInfo($"Detected LethalHands punch with force {force}"); } HealthManager.ProcessHit(__instance, force, playerWhoHit); if (flag) { return true; } return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Error in HitEnemyOnLocalClientPrefix: " + ex.Message)); return true; } } public static bool HitEnemyPrefix(EnemyAI __instance, int force, PlayerControllerB playerWhoHit) { try { if ((Object)(object)__instance == (Object)null || __instance.isEnemyDead) { return true; } string enemyName = __instance.enemyType.enemyName; string mobName = Plugin.RemoveInvalidCharacters(enemyName).ToUpper(); if (Plugin.IsModEnabledForEnemy(mobName) && !Plugin.CanMob(".Unimmortal", mobName)) { __instance.enemyHP = 999; Plugin.LogInfo("HitEnemyPrefix: Refreshed immortal enemy " + enemyName + " HP to 999"); return true; } return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Error in HitEnemyPrefix: " + ex.Message)); return true; } } public static bool SpikeRoofTrapPrefix(SpikeRoofTrap __instance, Collider other) { try { if (!((Component)other).CompareTag("Enemy")) { return true; } EnemyAICollisionDetect component = ((Component)other).gameObject.GetComponent<EnemyAICollisionDetect>(); if ((Object)(object)component?.mainScript == (Object)null) { return true; } EnemyAI mainScript = component.mainScript; if ((Object)(object)mainScript == (Object)null || mainScript.isEnemyDead) { return true; } string enemyName = mainScript.enemyType.enemyName; string mobName = Plugin.RemoveInvalidCharacters(enemyName).ToUpper(); if (!Plugin.IsModEnabledForEnemy(mobName)) { return true; } if (!Plugin.AllowSpikeTrapsToKillEnemies.Value) { Plugin.LogInfo("Spike trap blocked from killing " + enemyName + " due to global setting"); return false; } Plugin.LogInfo("Spike trap allowed to kill " + enemyName); if (DespawnConfiguration.Instance.ShouldDespawnEnemy(enemyName) && ((NetworkBehaviour)StartOfRound.Instance).IsHost) { ((MonoBehaviour)__instance).StartCoroutine(HandleSpikeKillCleanup(mainScript)); } return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Error in SpikeRoofTrapPrefix: " + ex.Message)); return true; } } [IteratorStateMachine(typeof(<HandleSpikeKillCleanup>d__7))] private static IEnumerator HandleSpikeKillCleanup(EnemyAI enemy) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <HandleSpikeKillCleanup>d__7(0) { enemy = enemy }; } private static float GetDespawnDelay() { SellBodiesCompatibility handler = ModCompatibilityManager.Instance.GetHandler<SellBodiesCompatibility>("Entity378.sellbodies"); if (handler != null && handler.IsInstalled) { return handler.GetDespawnDelay(); } return 0.5f; } private static void HandleKillCompatibility(EnemyAI enemy, int instanceId, string enemyName) { SellBodiesCompatibility handler = ModCompatibilityManager.Instance.GetHandler<SellBodiesCompatibility>("Entity378.sellbodies"); if (handler != null && handler.IsInstalled && handler.IsProblemEnemy(enemyName)) { handler.HandleProblemEnemyDeath(enemy); } HitmarkerCompatibility handler2 = ModCompatibilityManager.Instance.GetHandler<HitmarkerCompatibility>("com.github.zehsteam.Hitmarker"); if (handler2 != null && handler2.IsInstalled) { PlayerControllerB lastDamageSource = handler2.GetLastDamageSource(instanceId); if ((Object)(object)lastDamageSource != (Object)null) { handler2.NotifyEnemyKilled(enemy, lastDamageSource); } } } [HarmonyPostfix] public static void ShipLeavePostfix() { try { HitmarkerCompatibility handler = ModCompatibilityManager.Instance.GetHandler<HitmarkerCompatibility>("com.github.zehsteam.Hitmarker"); if (handler != null && handler.IsInstalled) { Plugin.LogInfo("Clearing Hitmarker compatibility data on level unload"); handler.ClearTracking(); } } catch (Exception ex) { Plugin.Log.LogError((object)("Error in ShipLeavePostfix: " + ex.Message)); } } public static void FinishGeneratingLevelPostfix() { try { BrutalCompanyMinusCompatibility handler = ModCompatibilityManager.Instance.GetHandler<BrutalCompanyMinusCompatibility>("SoftDiamond.BrutalCompanyMinusExtraReborn"); if (handler != null && handler.IsInstalled) { Plugin.LogInfo("Refreshing BrutalCompanyMinus compatibility data after level generation"); handler.RefreshBonusHp(); } } catch (Exception ex) { Plugin.Log.LogError((object)("Error in FinishGeneratingLevelPostfix: " + ex.Message)); } } } [BepInPlugin("nwnt.EverythingCanDieAlternative", "EverythingCanDieAlternative", "1.1.63")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public static List<EnemyType> enemies = new List<EnemyType>(); private static bool _infoLogsEnabled = true; private static readonly Dictionary<string, ConfigEntry<bool>> boolConfigCache = new Dictionary<string, ConfigEntry<bool>>(); private static readonly Dictionary<string, ConfigEntry<float>> floatConfigCache = new Dictionary<string, ConfigEntry<float>>(); private static readonly Dictionary<string, string> sanitizedNameCache = new Dictionary<string, string>(); public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static Harmony Harmony { get; private set; } public static ConfigEntry<bool> PatchCruiserDamage { get; private set; } public static ConfigEntry<float> CruiserDamageAtHighSpeeds { get; private set; } public static ConfigEntry<bool> AllowSpikeTrapsToKillEnemies { get; private set; } public static ConfigEntry<bool> ProtectImmortalEnemiesFromInstaKill { get; private set; } public bool IsSellBodiesModDetected => IsModInstalled("Entity378.sellbodies"); private void Awake() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Harmony = new Harmony("nwnt.EverythingCanDieAlternative"); try { _ = UIConfiguration.Instance; if (UIConfiguration.Instance != null && UIConfiguration.Instance.IsInitialized) { _infoLogsEnabled = UIConfiguration.Instance.ShouldLogInfo(); } AllowSpikeTrapsToKillEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("Traps", "AllowSpikeTrapsToKillEnemies", true, "If true, spike roof traps can kill enemies. If false, spike traps will not affect enemies managed by this mod."); ProtectImmortalEnemiesFromInstaKill = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ProtectImmortalEnemiesFromInstaKill", true, "If true, enemies set as immortal will be protected from insta-kill effects by setting their canDie property to false."); _ = DespawnConfiguration.Instance; _ = EnemyControlConfiguration.Instance; ModCompatibilityManager.Instance.Initialize(); Patches.Initialize(Harmony); if (UIConfiguration.Instance.IsConfigMenuEnabled()) { ConfigMenuPatch.Initialize(Harmony); Log.LogInfo((object)"EverythingCanDieAlternative v1.1.63 is loaded with network support and configuration menu!"); } else { Log.LogInfo((object)"EverythingCanDieAlternative v1.1.63 is loaded with network support (configuration menu disabled)"); } } catch (Exception ex) { Log.LogError((object)("Error initializing EverythingCanDieAlternative: " + ex.Message)); Log.LogError((object)("Stack trace: " + ex.StackTrace)); } } public bool IsModInstalled(string modId) { return ModCompatibilityManager.Instance.IsModInstalled(modId); } public static bool IsModEnabledForEnemy(string mobName) { return EnemyControlConfiguration.Instance.IsModEnabledForEnemy(mobName); } public static string RemoveInvalidCharacters(string source) { if (string.IsNullOrEmpty(source)) { return string.Empty; } if (sanitizedNameCache.TryGetValue(source, out var value)) { return value; } StringBuilder stringBuilder = new StringBuilder(source.Length); foreach (char c in source) { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { stringBuilder.Append(c); } } string text = stringBuilder.ToString(); sanitizedNameCache[source] = text; return text; } public static void LogInfo(string message) { if (_infoLogsEnabled) { Log.LogInfo((object)message); } } public static void LogError(string message) { Log.LogError((object)message); } public static void LogWarning(string message) { Log.LogWarning((object)message); } public static void RefreshLoggingState() { if (UIConfiguration.Instance != null && UIConfiguration.Instance.IsInitialized) { _infoLogsEnabled = UIConfiguration.Instance.ShouldLogInfo(); LogInfo("Logging state refreshed: info logs " + (_infoLogsEnabled ? "enabled" : "disabled")); } } public static bool CanMob(string identifier, string mobName) { try { string text = RemoveInvalidCharacters(mobName).ToUpper(); string text2 = text + identifier.ToUpper(); if (boolConfigCache.TryGetValue(text2, out var value)) { return value.Value; } ConfigEntry<bool> val = null; foreach (ConfigDefinition key in ((BaseUnityPlugin)Instance).Config.Keys) { if (RemoveInvalidCharacters(key.Key.ToUpper()).Equals(RemoveInvalidCharacters(text2))) { val = (ConfigEntry<bool>)(object)((BaseUnityPlugin)Instance).Config[key]; boolConfigCache[text2] = val; return val.Value; } } val = ((BaseUnityPlugin)Instance).Config.Bind<bool>("Mobs", text + identifier, true, "If true, " + mobName + " will be damageable"); boolConfigCache[text2] = val; return val.Value; } catch (Exception ex) { Log.LogError((object)("Error in config check for mob " + mobName + ": " + ex.Message)); return false; } } public static float GetMobHealth(string mobName, float defaultHealth) { try { if (defaultHealth <= 0f) { defaultHealth = 1f; LogInfo("Enforcing minimum health of 1 for " + mobName); } string text = RemoveInvalidCharacters(mobName).ToUpper(); string text2 = text + ".HEALTH"; if (floatConfigCache.TryGetValue(text2, out var value)) { float num = value.Value; if (num <= 0f) { num = 1f; value.Value = 1f; } return num; } ConfigEntry<float> val = null; foreach (ConfigDefinition key in ((BaseUnityPlugin)Instance).Config.Keys) { if (RemoveInvalidCharacters(key.Key.ToUpper()).Equals(text2)) { val = (ConfigEntry<float>)(object)((BaseUnityPlugin)Instance).Config[key]; floatConfigCache[text2] = val; float num2 = val.Value; if (num2 <= 0f) { num2 = 1f; val.Value = 1f; } return num2; } } val = ((BaseUnityPlugin)Instance).Config.Bind<float>("Mobs", text + ".Health", defaultHealth, "Health for " + mobName); floatConfigCache[text2] = val; return val.Value; } catch (Exception ex) { Log.LogError((object)("Error getting health for " + mobName + ": " + ex.Message)); return Math.Max(1f, defaultHealth); } } public static bool ShouldDespawn(string mobName) { return DespawnConfiguration.Instance.ShouldDespawnEnemy(mobName); } } public static class PluginInfo { public const string PLUGIN_GUID = "nwnt.EverythingCanDieAlternative"; public const string PLUGIN_NAME = "EverythingCanDieAlternative"; public const string PLUGIN_VERSION = "1.1.63"; } } namespace EverythingCanDieAlternative.UI { public class UIConfiguration { public static ConfigEntry<bool> EnableConfigMenu; public static ConfigEntry<bool> EnableInfoLogs; public static ConfigEntry<bool> ShowEnemyImages; private static UIConfiguration _instance; public static UIConfiguration Instance => _instance ?? (_instance = new UIConfiguration()); public bool IsInitialized { get; private set; } private UIConfiguration() { try { EnableConfigMenu = ((BaseUnityPlugin)Plugin.Instance).Config.Bind<bool>("General", "EnableConfigMenu", true, "If set to true, the config menu button will be shown in the main menu"); EnableInfoLogs = ((BaseUnityPlugin)Plugin.Instance).Config.Bind<bool>("General", "EnableInfoLogs", true, "If set to false, info logs will be suppressed to reduce console spam"); ShowEnemyImages = ((BaseUnityPlugin)Plugin.Instance).Config.Bind<bool>("General", "ShowEnemyImages", false, "If set to true, preview images for enemies will be shown in the config menu if available"); IsInitialized = true; } catch (Exception ex) { Plugin.Log.LogError((object)("Error initializing UI configuration: " + ex.Message)); IsInitialized = false; } } public bool IsConfigMenuEnabled() { if (!IsInitialized || EnableConfigMenu == null) { return true; } return EnableConfigMenu.Value; } public bool ShouldLogInfo() { if (!IsInitialized || EnableInfoLogs == null) { return true; } return EnableInfoLogs.Value; } public bool ShouldShowEnemyImages() { if (!IsInitialized || ShowEnemyImages == null) { return false; } return ShowEnemyImages.Value; } public void SetInfoLogsEnabled(bool enabled) { if (IsInitialized && EnableInfoLogs != null) { EnableInfoLogs.Value = enabled; Plugin.Log.LogInfo((object)("Info logs " + (enabled ? "enabled" : "disabled"))); Plugin.RefreshLoggingState(); } } public void SetConfigMenuEnabled(bool enabled) { if (IsInitialized && EnableConfigMenu != null) { EnableConfigMenu.Value = enabled; Plugin.Log.LogInfo((object)("Config menu " + (enabled ? "enabled" : "disabled"))); } } public void SetShowEnemyImages(bool enabled) { if (IsInitialized && ShowEnemyImages != null) { ShowEnemyImages.Value = enabled; Plugin.Log.LogInfo((object)("Enemy images " + (enabled ? "enabled" : "disabled"))); } } public void Save() { try { ((BaseUnityPlugin)Plugin.Instance).Config.Save(); ((BaseUnityPlugin)Plugin.Instance).Config.Reload(); EnemyControlConfiguration.Instance.ReloadConfig(); DespawnConfiguration.Instance.ReloadConfig(); Plugin.LogInfo("All UI settings saved and reloaded"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error saving UI settings: " + ex.Message)); } } public void ReloadAllConfigs() { try { ((BaseUnityPlugin)Plugin.Instance).Config.Reload(); EnemyControlConfiguration.Instance.ReloadConfig(); DespawnConfiguration.Instance.ReloadConfig(); Plugin.LogInfo("All configs reloaded from files"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error reloading configs: " + ex.Message)); } } } public static class ConfigBridge { private static readonly Regex sectionRegex = new Regex("^\\[(.*)\\]$", RegexOptions.Compiled); private static string pluginConfigPath = Path.Combine(Paths.ConfigPath, "nwnt.EverythingCanDieAlternative.cfg"); private static string enemyControlConfigPath = Path.Combine(Paths.ConfigPath, "nwnt.EverythingCanDieAlternative_Enemy_Control.cfg"); private static string despawnConfigPath = Path.Combine(Paths.ConfigPath, "nwnt.EverythingCanDieAlternative_Despawn_Rules.cfg"); private static Dictionary<string, Dictionary<string, object>> cachedConfigEntries = new Dictionary<string, Dictionary<string, object>>(); private static bool isLoading = false; public static List<EnemyConfigData> LoadAllEnemyConfigs() { if (isLoading) { return new List<EnemyConfigData>(); } isLoading = true; List<EnemyConfigData> list = new List<EnemyConfigData>(); try { cachedConfigEntries.Clear(); LogFileDetails(pluginConfigPath, "Main plugin config"); LogFileDetails(enemyControlConfigPath, "Enemy control config"); LogFileDetails(despawnConfigPath, "Despawn config"); ReadConfigFileDirectly(pluginConfigPath, "Mobs"); ReadConfigFileDirectly(enemyControlConfigPath, "Enemies"); ReadConfigFileDirectly(despawnConfigPath, "Enemies"); List<string> allConfiguredEnemyNames = GetAllConfiguredEnemyNames(); if (allConfiguredEnemyNames.Count == 0) { Plugin.Log.LogWarning((object)"No configured enemies found in any config files. Start a round to generate configurations."); isLoading = false; return list; } Plugin.LogInfo($"Found {allConfiguredEnemyNames.Count} configured enemies in config files"); foreach (string item in allConfiguredEnemyNames) { bool isEnabled = false; bool canDie = true; bool shouldDespawn = true; float health = 3f; if (cachedConfigEntries.TryGetValue(item, out var value)) { if (value.TryGetValue(".ENABLED", out var value2) && value2 is bool flag) { isEnabled = flag; } if (value.TryGetValue(".UNIMMORTAL", out var value3) && value3 is bool flag2) { canDie = flag2; } if (value.TryGetValue(".DESPAWN", out var value4) && value4 is bool flag3) { shouldDespawn = flag3; } if (value.TryGetValue(".HEALTH", out var value5)) { float result; if (value5 is float num) { health = num; } else if (value5 is int num2) { health = num2; } else if (value5 is string s && float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { health = result; } } } list.Add(new EnemyConfigData(item, isEnabled, canDie, shouldDespawn, health)); } Plugin.LogInfo($"Loaded {list.Count} enemy configurations successfully"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Error loading enemy configurations: {arg}"); } finally { isLoading = false; } return list; } private static void LogFileDetails(string path, string description) { if (File.Exists(path)) { FileInfo fileInfo = new FileInfo(path); } } private static void ReadConfigFileDirectly(string path, string targetSection) { if (!File.Exists(path)) { return; } try { string[] array = File.ReadAllLines(path); Plugin.LogInfo($"Reading config file {Path.GetFileName(path)} ({array.Length} lines)"); string text = ""; int num = 0; string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); if (string.IsNullOrWhiteSpace(text3) || text3.StartsWith("#") || text3.StartsWith("//")) { continue; } Match match = sectionRegex.Match(text3); if (match.Success) { text = match.Groups[1].Value; } else if (!(text != targetSection)) { int num2 = text3.IndexOf('='); if (num2 > 0) { string key = text3.Substring(0, num2).Trim(); string value = text3.Substring(num2 + 1).Trim(); ProcessConfigEntry(text, key, value); num++; } } } Plugin.LogInfo($"Found {num} entries in section [{targetSection}]"); } catch (Exception ex) { Plugin.Log.LogError((object)("Error reading config file " + path + ": " + ex.Message)); } } private static void ProcessConfigEntry(string section, string key, string value) { string text = null; string text2 = null; if (section == "Mobs") { int num = key.LastIndexOf('.'); if (num > 0) { text = key.Substring(0, num).ToUpper(); text2 = key.Substring(num).ToUpper(); } } else if (section == "Enemies") { int num2 = key.LastIndexOf('.'); if (num2 > 0) { text = key.Substring(0, num2).ToUpper(); text2 = key.Substring(num2).ToUpper(); } } if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) { return; } if (!cachedConfigEntries.ContainsKey(text)) { cachedConfigEntries[text] = new Dictionary<string, object>(); } if (text2 == ".ENABLED" || text2 == ".UNIMMORTAL" || text2 == ".DESPAWN") { if (bool.TryParse(value, out var result)) { cachedConfigEntries[text][text2] = result; } } else if (text2 == ".HEALTH") { if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { cachedConfigEntries[text][text2] = result2; return; } cachedConfigEntries[text][text2] = value; Plugin.Log.LogWarning((object)("Could not parse health value " + value + " for " + text)); } } private static List<string> GetAllConfiguredEnemyNames() { return cachedConfigEntries.Keys.ToList(); } private static bool GetCachedBoolValue(string enemyName, string suffix, bool defaultValue) { if (cachedConfigEntries.TryGetValue(enemyName, out var value) && value.TryGetValue(suffix, out var value2) && value2 is bool result) { return result; } return defaultValue; } private static float GetCachedFloatValue(string enemyName, string suffix, float defaultValue) { if (cachedConfigEntries.TryGetValue(enemyName, out var value) && value.TryGetValue(suffix, out var value2)) { if (value2 is float result) { return result; } if (value2 is int num) { return num; } if (value2 is string s && float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return result2; } Plugin.Log.LogWarning((object)string.Format("Invalid health value for {0}: {1} (type: {2})", enemyName, value2, value2?.GetType().Name ?? "null")); } return defaultValue; } public static void SaveEnemyConfig(EnemyConfigData config) { if (config == null) { return; } string sanitizedName = config.SanitizedName; string name = config.Name; if (!cachedConfigEntries.ContainsKey(sanitizedName)) { Plugin.Log.LogWarning((object)("Cannot save config for " + name + " as no existing entries were found")); return; } try { bool flag = false; bool flag2 = false; bool flag3 = false; if (cachedConfigEntries[sanitizedName].ContainsKey(".UNIMMORTAL") || cachedConfigEntries[sanitizedName].ContainsKey(".HEALTH")) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); if (cachedConfigEntries[sanitizedName].ContainsKey(".UNIMMORTAL")) { dictionary[sanitizedName + ".Unimmortal"] = config.CanDie.ToString().ToLower(); } if (cachedConfigEntries[sanitizedName].ContainsKey(".HEALTH")) { dictionary[sanitizedName + ".Health"] = config.Health.ToString(CultureInfo.InvariantCulture); } flag = UpdateConfigFileDirectly(pluginConfigPath, "Mobs", dictionary); } if (cachedConfigEntries[sanitizedName].ContainsKey(".ENABLED")) { flag2 = UpdateConfigFileDirectly(enemyControlConfigPath, "Enemies", new Dictionary<string, string> { { sanitizedName + ".Enabled", config.IsEnabled.ToString().ToLower() } }); } if (cachedConfigEntries[sanitizedName].ContainsKey(".DESPAWN")) { flag3 = UpdateConfigFileDirectly(despawnConfigPath, "Enemies", new Dictionary<string, string> { { sanitizedName + ".Despawn", config.ShouldDespawn.ToString().ToLower() } }); } cachedConfigEntries[sanitizedName][".ENABLED"] = config.IsEnabled; cachedConfigEntries[sanitizedName][".UNIMMORTAL"] = config.CanDie; cachedConfigEntries[sanitizedName][".HEALTH"] = config.Health; cachedConfigEntries[sanitizedName][".DESPAWN"] = config.ShouldDespawn; } catch (Exception ex) { Plugin.Log.LogError((object)("Error saving configuration: " + ex.Message)); } } private static bool UpdateConfigFileDirectly(string path, string section, Dictionary<string, string> updates) { if (!File.Exists(path)) { return false; } try { string[] array = File.ReadAllLines(path); List<string> list = new List<string>(); string text = ""; bool flag = false; Dictionary<string, bool> dictionary = new Dictionary<string, bool>(); foreach (string key in updates.Keys) { dictionary[key.ToUpper()] = false; } string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); Match match = sectionRegex.Match(text3); if (match.Success) { text = match.Groups[1].Value; list.Add(text2); } else if (text == section) { bool flag2 = false; int num = text3.IndexOf('='); if (num > 0) { string text4 = text3.Substring(0, num).Trim(); foreach (string key2 in updates.Keys) { if (text4.Equals(key2, StringComparison.OrdinalIgnoreCase) || Plugin.RemoveInvalidCharacters(text4.ToUpper()) == Plugin.RemoveInvalidCharacters(key2.ToUpper())) { list.Add(text4 + " = " + updates[key2]); dictionary[key2.ToUpper()] = true; flag = true; flag2 = true; break; } } } if (!flag2) { list.Add(text2); } } else { list.Add(text2); } } bool flag3 = false; foreach (KeyValuePair<string, bool> item in dictionary) { if (!item.Value) { flag3 = true; break; } } if (flag3) { bool flag4 = false; for (int j = 0; j < list.Count; j++) { if (list[j].Trim() == "[" + section + "]") { flag4 = true; break; } } if (!flag4) { if (list.Count > 0 && !string.IsNullOrWhiteSpace(list[list.Count - 1])) { list.Add(""); } list.Add("[" + section + "]"); } foreach (KeyValuePair<string, bool> entry in dictionary) { if (!entry.Value) { string text5 = updates.Keys.First((string k) => k.ToUpper() == entry.Key); string text6 = updates[text5]; list.Add(text5 + " = " + text6); flag = true; } } } if (flag) { File.WriteAllLines(path, list); } return flag; } catch (Exception ex) { Plugin.Log.LogError((object)("Error updating config file " + path + ": " + ex.Message)); return false; } } } public class ConfigMenuManager : MonoBehaviour { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Comparison<EnemyConfigData> <>9__17_0; public static UnityAction <>9__22_0; public static UnityAction<bool> <>9__22_1; public static UnityAction<bool> <>9__22_2; public static UnityAction<bool> <>9__22_3; public static UnityAction<bool> <>9__25_0; public static UnityAction<bool> <>9__25_1; public static Func<KeyValuePair<string, GameObject>, bool> <>9__29_0; internal int <RefreshEnemyData>b__17_0(EnemyConfigData a, EnemyConfigData b) { return string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); } internal void <CreateMenuPrefab>b__22_0() { PlayCancelSFX(); menuPrefab.SetActive(false); } internal void <CreateMenuPrefab>b__22_1(bool isHideMenu) { PlayConfirmSFX(); UIConfiguration.Instance.SetConfigMenuEnabled(!isHideMenu); UIConfiguration.Instance.Save(); } internal void <CreateMenuPrefab>b__22_2(bool isLessLogs) { PlayConfirmSFX(); UIConfiguration.Instance.SetInfoLogsEnabled(!isLessLogs); UIConfiguration.Instance.Save(); } internal void <CreateMenuPrefab>b__22_3(bool showImages) { PlayConfirmSFX(); UIConfiguration.Instance.SetShowEnemyImages(showImages); UIConfiguration.Instance.Save(); ConfigMenuManager component = menuPrefab.GetComponent<ConfigMenuManager>(); if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.selectedEnemyName)) { component.UpdateConfigPanel(); } } internal void <ShowGlobalSettings>b__25_0(bool allowSpikeTraps) { Plugin.AllowSpikeTrapsToKillEnemies.Value = allowSpikeTraps; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); PlayConfirmSFX(); Plugin.LogInfo("Spike trap kills " + (allowSpikeTraps ? "enabled" : "disabled")); } internal void <ShowGlobalSettings>b__25_1(bool protectImmortals) { Plugin.ProtectImmortalEnemiesFromInstaKill.Value = protectImmortals; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); PlayConfirmSFX(); Plugin.LogInfo("Immortal enemy insta-kill protection " + (protectImmortals ? "enabled" : "disabled")); } internal bool <FilterEnemyList>b__29_0(KeyValuePair<string, GameObject> e) { return e.Value.activeSelf; } } [CompilerGenerated] private sealed class <DelayedRefresh>d__21 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public ConfigMenuManager <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedRefresh>d__21(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; Plugin.LogInfo("Starting delayed refresh"); <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return true; case 1: <>1__state = -1; <>4__this.RefreshEnemyData(); refreshScheduled = false; Plugin.LogInfo("Delayed refresh completed"); 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(); } } private static GameObject menuPrefab; private GameObject menuPanel; private RectTransform enemyListContent; private GameObject enemyConfigPanel; private GameObject globalSettingsButton; private bool isShowingGlobalSettings = false; private List<EnemyConfigData> enemyConfigs = new List<EnemyConfigData>(); private Dictionary<string, GameObject> enemyEntries = new Dictionary<string, GameObject>(); private readonly Stack<GameObject> enemyEntryPool = new Stack<GameObject>(); private const int INITIAL_POOL_SIZE = 20; private string selectedEnemyName; private const float ENTRY_HEIGHT = 35f; private static bool refreshScheduled; private TMP_InputField searchInputField; private string lastSearchText = ""; private void ClearSearch() { if ((Object)(object)searchInputField != (Object)null) { searchInputField.text = ""; lastSearchText = ""; FilterEnemyList(lastSearchText); } } private void ScheduleRefresh() { if (!refreshScheduled) { refreshScheduled = true; ClearSearch(); ((MonoBehaviour)this).StartCoroutine(DelayedRefresh()); } } public void RefreshEnemyData() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown Plugin.LogInfo("RefreshEnemyData called"); foreach (Transform item in (Transform)enemyListContent) { Transform val = item; ((Component)val).gameObject.SetActive(false); enemyEntryPool.Push(((Component)val).gameObject); } enemyEntries.Clear(); enemyConfigs = ConfigBridge.LoadAllEnemyConfigs(); enemyConfigs.Sort((EnemyConfigData a, EnemyConfigData b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); Plugin.LogInfo($"Sorted {enemyConfigs.Count} enemies alphabetically"); foreach (EnemyConfigData enemyConfig in enemyConfigs) { CreateEnemyListEntry(enemyConfig); } FilterEnemyList(lastSearchText); selectedEnemyName = null; UpdateConfigPanel(); Plugin.LogInfo($"Loaded {enemyConfigs.Count} enemy configurations"); } private static void PlayConfirmSFX() { MenuManager val = Object.FindObjectOfType<MenuManager>(); if ((Object)(object)val != (Object)null && (Object)(object)val.MenuAudio != (Object)null) { val.PlayConfirmSFX(); } } private static void PlayCancelSFX() { MenuManager val = Object.FindObjectOfType<MenuManager>(); if ((Object)(object)val != (Object)null && (Object)(object)val.MenuAudio != (Object)null) { val.PlayCancelSFX(); } } public static void ToggleConfigMenu() { if (!UIConfiguration.Instance.IsConfigMenuEnabled()) { Plugin.LogWarning("Attempted to open config menu, but it's disabled in settings"); return; } Plugin.LogInfo("ToggleConfigMenu called"); if ((Object)(object)menuPrefab == (Object)null) { CreateMenuPrefab(); } bool activeSelf = menuPrefab.activeSelf; menuPrefab.SetActive(!activeSelf); if (!activeSelf) { Plugin.LogInfo("Menu opened, scheduling refresh"); ConfigMenuManager component = menuPrefab.GetComponent<ConfigMenuManager>(); component.ScheduleRefresh(); } } [IteratorStateMachine(typeof(<DelayedRefresh>d__21))] private IEnumerator DelayedRefresh() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedRefresh>d__21(0) { <>4__this = this }; } private static void CreateMenuPrefab() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0086: 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_0130: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_05c3: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0516: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_0544: Unknown result type (might be due to invalid IL or missing references) //IL_055b: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Unknown result type (might be due to invalid IL or missing references) //IL_061a: Unknown result type (might be due to invalid IL or missing references) //IL_0631: Unknown result type (might be due to invalid IL or missing references) //IL_0648: Unknown result type (might be due to invalid IL or missing references) //IL_065f: Unknown result type (might be due to invalid IL or missing references) //IL_0680: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_06c0: Unknown result type (might be due to invalid IL or missing references) //IL_06d7: Unknown result type (might be due to invalid IL or missing references) //IL_06ee: Unknown result type (might be due to invalid IL or missing references) //IL_0705: Unknown result type (might be due to invalid IL or missing references) //IL_0824: Unknown result type (might be due to invalid IL or missing references) //IL_082e: Expected O, but got Unknown //IL_0754: Unknown result type (might be due to invalid IL or missing references) //IL_076b: Unknown result type (might be due to invalid IL or missing references) //IL_0782: Unknown result type (might be due to invalid IL or missing references) //IL_0799: Unknown result type (might be due to invalid IL or missing references) //IL_07b0: Unknown result type (might be due to invalid IL or missing references) //IL_05a0: Unknown result type (might be due to invalid IL or missing references) //IL_09c1: Unknown result type (might be due to invalid IL or missing references) //IL_0864: Unknown result type (might be due to invalid IL or missing references) //IL_087b: Unknown result type (might be due to invalid IL or missing references) //IL_0892: Unknown result type (might be due to invalid IL or missing references) //IL_08a9: Unknown result type (might be due to invalid IL or missing references) //IL_08c0: Unknown result type (might be due to invalid IL or missing references) //IL_07ff: Unknown result type (might be due to invalid IL or missing references) //IL_0a01: Unknown result type (might be due to invalid IL or missing references) //IL_0a18: Unknown result type (might be due to invalid IL or missing references) //IL_0a2f: Unknown result type (might be due to invalid IL or missing references) //IL_0a46: Unknown result type (might be due to invalid IL or missing references) //IL_0a5d: Unknown result type (might be due to invalid IL or missing references) //IL_08e5: Unknown result type (might be due to invalid IL or missing references) //IL_08ea: Unknown result type (might be due to invalid IL or missing references) //IL_0902: Unknown result type (might be due to invalid IL or missing references) //IL_0923: Unknown result type (might be due to invalid IL or missing references) //IL_0944: Unknown result type (might be due to invalid IL or missing references) //IL_0951: Unknown result type (might be due to invalid IL or missing references) //IL_0995: Unknown result type (might be due to invalid IL or missing references) //IL_0b2c: Unknown result type (might be due to invalid IL or missing references) //IL_0b33: Expected O, but got Unknown //IL_0b5d: Unknown result type (might be due to invalid IL or missing references) //IL_0b74: Unknown result type (might be due to invalid IL or missing references) //IL_0b8b: Unknown result type (might be due to invalid IL or missing references) //IL_0ba2: Unknown result type (might be due to invalid IL or missing references) //IL_0bb9: Unknown result type (might be due to invalid IL or missing references) //IL_0be3: Unknown result type (might be due to invalid IL or missing references) //IL_0bf3: Unknown result type (might be due to invalid IL or missing references) //IL_0bfa: Expected O, but got Unknown //IL_0c24: Unknown result type (might be due to invalid IL or missing references) //IL_0c3b: Unknown result type (might be due to invalid IL or missing references) //IL_0c52: Unknown result type (might be due to invalid IL or missing references) //IL_0c69: Unknown result type (might be due to invalid IL or missing references) //IL_0c80: Unknown result type (might be due to invalid IL or missing references) //IL_0cc4: Unknown result type (might be due to invalid IL or missing references) //IL_0cea: Unknown result type (might be due to invalid IL or missing references) //IL_0cf1: Expected O, but got Unknown //IL_0d1b: Unknown result type (might be due to invalid IL or missing references) //IL_0d32: Unknown result type (might be due to invalid IL or missing references) //IL_0d49: Unknown result type (might be due to invalid IL or missing references) //IL_0d60: Unknown result type (might be due to invalid IL or missing references) //IL_0d97: Unknown result type (might be due to invalid IL or missing references) //IL_0db4: Unknown result type (might be due to invalid IL or missing references) //IL_0dbb: Expected O, but got Unknown //IL_0de5: Unknown result type (might be due to invalid IL or missing references) //IL_0dfc: Unknown result type (might be due to invalid IL or missing references) //IL_0e13: Unknown result type (might be due to invalid IL or missing references) //IL_0e2a: Unknown result type (might be due to invalid IL or missing references) //IL_0e6e: Unknown result type (might be due to invalid IL or missing references) //IL_0ed9: Unknown result type (might be due to invalid IL or missing references) //IL_0ef0: Unknown result type (might be due to invalid IL or missing references) //IL_0f11: Unknown result type (might be due to invalid IL or missing references) //IL_0f51: Unknown result type (might be due to invalid IL or missing references) //IL_0f68: Unknown result type (might be due to invalid IL or missing references) //IL_0f7f: Unknown result type (might be due to invalid IL or missing references) //IL_0f96: Unknown result type (might be due to invalid IL or missing references) //IL_1042: Unknown result type (might be due to invalid IL or missing references) //IL_0fef: Unknown result type (might be due to invalid IL or missing references) //IL_1006: Unknown result type (might be due to invalid IL or missing references) //IL_1013: Unknown result type (might be due to invalid IL or missing references) //IL_1020: Unknown result type (might be due to invalid IL or missing references) //IL_1075: Unknown result type (might be due to invalid IL or missing references) //IL_108c: Unknown result type (might be due to invalid IL or missing references) //IL_10a3: Unknown result type (might be due to invalid IL or missing references) //IL_10ba: Unknown result type (might be due to invalid IL or missing references) //IL_10f3: Unknown result type (might be due to invalid IL or missing references) //IL_1166: Unknown result type (might be due to invalid IL or missing references) try { Plugin.LogInfo("Creating menu prefab"); menuPrefab = new GameObject("ECDAConfigMenu"); ConfigMenuManager menuManager = menuPrefab.AddComponent<ConfigMenuManager>(); menuPrefab.SetActive(false); Object.DontDestroyOnLoad((Object)(object)menuPrefab); Canvas val = menuPrefab.AddComponent<Canvas>(); val.renderMode = (RenderMode)0; val.sortingOrder = 1000; CanvasScaler val2 = menuPrefab.AddComponent<CanvasScaler>(); val2.uiScaleMode = (ScaleMode)1; val2.referenceResolution = new Vector2(1920f, 1080f); menuPrefab.AddComponent<GraphicRaycaster>(); GameObject val3 = UIHelper.CreatePanel(menuPrefab.transform, "Overlay", new Vector2(2000f, 2000f)); if ((Object)(object)val3 == (Object)null) { Plugin.LogError("Failed to create overlay panel"); return; } Image component = val3.GetComponent<Image>(); if ((Object)(object)component != (Object)null) { ((Graphic)component).color = new Color(0f, 0f, 0f, 0.7f); } GameObject val4 = UIHelper.CreatePanel(menuPrefab.transform, "MainPanel", new Vector2(800f, 600f)); if ((Object)(object)val4 == (Object)null) { Plugin.LogError("Failed to create main panel"); return; } menuManager.menuPanel = val4; Image component2 = val4.GetComponent<Image>(); if ((Object)(object)component2 != (Object)null) { ((Graphic)component2).color = new Color(0.1f, 0.1f, 0.1f, 0.95f); } Transform transform = val4.transform; object obj = <>c.<>9__22_0; if (obj == null) { UnityAction val5 = delegate { PlayCancelSFX(); menuPrefab.SetActive(false); }; <>c.<>9__22_0 = val5; obj = (object)val5; } GameObject val6 = UIHelper.CreateButton(transform, "CloseButton", "X", (UnityAction)obj); if ((Object)(object)val6 != (Object)null) { RectTransform component3 = val6.GetComponent<RectTransform>(); component3.anchorMin = new Vector2(1f, 1f); component3.anchorMax = new Vector2(1f, 1f); component3.pivot = new Vector2(1f, 1f); component3.sizeDelta = new Vector2(40f, 40f); component3.anchoredPosition = new Vector2(-10f, -10f); } GameObject val7 = UIHelper.CreateYesNoSelector(val4.transform, "HideMenuSelector", "Hide Menu:", !UIConfiguration.Instance.IsConfigMenuEnabled(), delegate(bool isHideMenu) { PlayConfirmSFX(); UIConfiguration.Instance.SetConfigMenuEnabled(!isHideMenu); UIConfiguration.Instance.Save(); }); if ((Object)(object)val7 != (Object)null) { RectTransform component4 = val7.GetComponent<RectTransform>(); component4.anchorMin = new Vector2(0f, 1f); component4.anchorMax = new Vector2(0f, 1f); component4.pivot = new Vector2(0f, 1f); component4.sizeDelta = new Vector2(160f, 30f); component4.anchoredPosition = new Vector2(40f, -15f); Transform obj2 = val7.transform.Find("Label"); RectTransform val8 = ((obj2 != null) ? ((Component)obj2).GetComponent<RectTransform>() : null); if ((Object)(object)val8 != (Object)null) { val8.sizeDelta = new Vector2(80f, 30f); } } GameObject val9 = UIHelper.CreateYesNoSelector(val4.transform, "LessLogsSelector", "Less Logs:", !UIConfiguration.Instance.ShouldLogInfo(), delegate(bool isLessLogs) { PlayConfirmSFX(); UIConfiguration.Instance.SetInfoLogsEnabled(!isLessLogs); UIConfiguration.Instance.Save(); }); if ((Object)(object)val9 != (Object)null) { RectTransform component5 = val9.GetComponent<RectTransform>(); component5.anchorMin = new Vector2(0f, 1f); component5.anchorMax = new Vector2(0f, 1f); component5.pivot = new Vector2(0f, 1f); component5.sizeDelta = new Vector2(150f, 30f); component5.anchoredPosition = new Vector2(260f, -15f); Transform obj3 = val9.transform.Find("Label"); RectTransform val10 = ((obj3 != null) ? ((Component)obj3).GetComponent<RectTransform>() : null); if ((Object)(object)val10 != (Object)null) { val10.sizeDelta = new Vector2(70f, 30f); } } GameObject val11 = UIHelper.CreateYesNoSelector(val4.transform, "ShowImagesSelector", "Show Images:", UIConfiguration.Instance.ShouldShowEnemyImages(), delegate(bool showImages) { PlayConfirmSFX(); UIConfiguration.Instance.SetShowEnemyImages(showImages); UIConfiguration.Instance.Save(); ConfigMenuManager component20 = menuPrefab.GetComponent<ConfigMenuManager>(); if ((Object)(object)component20 != (Object)null && !string.IsNullOrEmpty(component20.selectedEnemyName)) { component20.UpdateConfigPanel(); } }); if ((Object)(object)val11 != (Object)null) { RectTransform component6 = val11.GetComponent<RectTransform>(); component6.anchorMin = new Vector2(0f, 1f); component6.anchorMax = new Vector2(0f, 1f); component6.pivot = new Vector2(0f, 1f); component6.sizeDelta = new Vector2(170f, 30f); component6.anchoredPosition = new Vector2(470f, -15f); Transform obj4 = val11.transform.Find("Label"); RectTransform val12 = ((obj4 != null) ? ((Component)obj4).GetComponent<RectTransform>() : null); if ((Object)(object)val12 != (Object)null) { val12.sizeDelta = new Vector2(100f, 30f); } } GameObject val13 = UIHelper.CreatePanel(val4.transform, "ContentPanel", new Vector2(780f, 520f)); if ((Object)(object)val13 == (Object)null) { Plugin.LogError("Failed to create content panel"); return; } RectTransform component7 = val13.GetComponent<RectTransform>(); component7.anchorMin = new Vector2(0f, 0f); component7.anchorMax = new Vector2(1f, 1f); component7.pivot = new Vector2(0.5f, 0.5f); component7.offsetMin = new Vector2(10f, 60f); component7.offsetMax = new Vector2(-10f, -50f); GameObject val14 = UIHelper.CreatePanel(val13.transform, "EnemyListPanel", new Vector2(250f, 520f)); if ((Object)(object)val14 == (Object)null) { Plugin.LogError("Failed to create enemy list panel"); return; } RectTransform component8 = val14.GetComponent<RectTransform>(); component8.anchorMin = new Vector2(0f, 0f); component8.anchorMax = new Vector2(0f, 1f); component8.pivot = new Vector2(0f, 0.5f); component8.sizeDelta = new Vector2(250f, 0f); GameObject val15 = UIHelper.CreateText(val14.transform, "ListLabel", "ENEMIES", (TextAlignmentOptions)514); if ((Object)(object)val15 != (Object)null) { RectTransform component9 = val15.GetComponent<RectTransform>(); component9.anchorMin = new Vector2(0f, 1f); component9.anchorMax = new Vector2(1f, 1f); component9.pivot = new Vector2(0.5f, 1f); component9.sizeDelta = new Vector2(0f, 30f); component9.anchoredPosition = new Vector2(0f, -5f); TextMeshProUGUI component10 = val15.GetComponent<TextMeshProUGUI>(); if ((Object)(object)component10 != (Object)null) { ((TMP_Text)component10).fontSize = 16f; ((TMP_Text)component10).fontStyle = (FontStyles)1; ((Graphic)component10).color = new Color(1f, 0.9f, 0.5f, 1f); } } GameObject val16 = UIHelper.CreateButton(val14.transform, "GlobalSettingsButton", "Global Settings", (UnityAction)delegate { PlayConfirmSFX(); menuManager.ToggleGlobalSettings(); }); if ((Object)(object)val16 != (Object)null) { menuManager.globalSettingsButton = val16; RectTransform component11 = val16.GetComponent<RectTransform>(); component11.anchorMin = new Vector2(0f, 1f); component11.anchorMax = new Vector2(1f, 1f); component11.pivot = new Vector2(0.5f, 1f); component11.sizeDelta = new Vector2(-20f, 35f); component11.anchoredPosition = new Vector2(0f, -40f); Button component12 = val16.GetComponent<Button>(); if ((Object)(object)component12 != (Object)null) { ColorBlock colors = ((Selectable)component12).colors; ((ColorBlock)(ref colors)).normalColor = new Color(0.3f, 0.3f, 0.3f, 1f); ((ColorBlock)(ref colors)).highlightedColor = new Color(0.4f, 0.4f, 0.4f, 1f); ((ColorBlock)(ref colors)).pressedColor = new Color(0.2f, 0.2f, 0.2f, 1f); ((Selectable)component12).colors = colors; } TextMeshProUGUI componentInChildren = val16.GetComponentInChildren<TextMeshProUGUI>(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).fontSize = 14f; ((Graphic)componentInChildren).color = new Color(1f, 0.9f, 0.5f, 1f); ((TMP_Text)componentInChildren).fontStyle = (FontStyles)1; } } GameObject val17 = UIHelper.CreateScrollView(val14.transform, "EnemyScrollView", new Vector2(240f, 450f)); if ((Object)(object)val17 == (Object)null) { Plugin.LogError("Failed to create enemy scroll view"); return; } RectTransform component13 = val17.GetComponent<RectTransform>(); component13.anchorMin = new Vector2(0.5f, 0.5f); component13.anchorMax = new Vector2(0.5f, 0.5f); component13.pivot = new Vector2(0.5f, 0.5f); component13.anchoredPosition = new Vector2(0f, -90f); component13.sizeDelta = new Vector2(240f, 385f); ScrollRect component14 = val17.GetComponent<ScrollRect>(); if ((Object)(object)component14 != (Object)null) { component14.scrollSensitivity = 15f; } Transform val18 = val17.transform.Find("Viewport"); if ((Object)(object)val18 == (Object)null) { Plugin.LogError("Viewport not found in scroll view"); return; } Transform val19 = val18.Find("Content"); if ((Object)(object)val19 == (Object)null) { Plugin.LogError("Content not found in viewport"); return; } menuManager.enemyListContent = ((Component)val19).GetComponent<RectTransform>(); if ((Object)(object)menuManager.enemyListContent == (Object)null) { Plugin.LogError("Failed to get RectTransform for enemyListContent"); return; } GameObject val20 = new GameObject("SearchContainer"); val20.transform.SetParent(val14.transform, false); RectTransform val21 = val20.AddComponent<RectTransform>(); val21.anchorMin = new Vector2(0f, 1f); val21.anchorMax = new Vector2(1f, 1f); val21.pivot = new Vector2(0.5f, 1f); val21.sizeDelta = new Vector2(-20f, 30f); val21.anchoredPosition = new Vector2(0f, -80f); Image val22 = val20.AddComponent<Image>(); ((Graphic)val22).color = new Color(0.12f, 0.12f, 0.12f, 1f); GameObject val23 = new GameObject("SearchIcon"); val23.transform.SetParent(val20.transform, false); RectTransform val24 = val23.AddComponent<RectTransform>(); val24.anchorMin = new Vector2(0f, 0.5f); val24.anchorMax = new Vector2(0f, 0.5f); val24.pivot = new Vector2(0.5f, 0.5f); val24.sizeDelta = new Vector2(20f, 20f); val24.anchoredPosition = new Vector2(15f, 0f); TextMeshProUGUI val25 = val23.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val25).text = ">"; ((TMP_Text)val25).fontSize = 16f; ((Graphic)val25).color = new Color(0.7f, 0.7f, 0.7f, 1f); ((TMP_Text)val25).alignment = (TextAlignmentOptions)514; TMP_InputField val26 = val20.AddComponent<TMP_InputField>(); GameObject val27 = new GameObject("Text"); val27.transform.SetParent(val20.transform, false); RectTransform val28 = val27.AddComponent<RectTransform>(); val28.anchorMin = new Vector2(0f, 0f); val28.anchorMax = new Vector2(1f, 1f); val28.offsetMin = new Vector2(35f, 2f); val28.offsetMax = new Vector2(-5f, -2f); TextMeshProUGUI val29 = val27.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val29).fontSize = 14f; ((Graphic)val29).color = new Color(0.9f, 0.9f, 0.9f, 1f); ((TMP_Text)val29).alignment = (TextAlignmentOptions)513; GameObject val30 = new GameObject("Placeholder"); val30.transform.SetParent(val20.transform, false); RectTransform val31 = val30.AddComponent<RectTransform>(); val31.anchorMin = new Vector2(0f, 0f); val31.anchorMax = new Vector2(1f, 1f); val31.offsetMin = new Vector2(35f, 2f); val31.offsetMax = new Vector2(-5f, -2f); TextMeshProUGUI val32 = val30.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val32).text = "Search enemies..."; ((TMP_Text)val32).fontSize = 14f; ((Graphic)val32).color = new Color(0.5f, 0.5f, 0.5f, 1f); ((TMP_Text)val32).alignment = (TextAlignmentOptions)513; val26.textComponent = (TMP_Text)(object)val29; val26.placeholder = (Graphic)(object)val32; val26.text = ""; menuManager.searchInputField = val26; ((UnityEvent<string>)(object)val26.onValueChanged).AddListener((UnityAction<string>)delegate(string searchText) { menuManager.FilterEnemyList(searchText); }); component13.anchoredPosition = new Vector2(0f, -70f); component13.sizeDelta = new Vector2(240f, 405f); GameObject val33 = UIHelper.CreatePanel(val13.transform, "ConfigPanel", new Vector2(500f, 520f)); if ((Object)(object)val33 == (Object)null) { Plugin.LogError("Failed to create config panel"); return; } RectTransform component15 = val33.GetComponent<RectTransform>(); component15.anchorMin = new Vector2(1f, 0f); component15.anchorMax = new Vector2(1f, 1f); component15.pivot = new Vector2(1f, 0.5f); component15.sizeDelta = new Vector2(500f, 0f); menuManager.enemyConfigPanel = val33; GameObject val34 = UIHelper.CreateText(val33.transform, "NoSelection", "Select an enemy from the list", (TextAlignmentOptions)514); if ((Object)(object)val34 != (Object)null) { RectTransform component16 = val34.GetComponent<RectTransform>(); component16.anchorMin = new Vector2(0f, 0f); component16.anchorMax = new Vector2(1f, 1f); component16.offsetMin = Vector2.zero; component16.offsetMax = Vector2.zero; } GameObject val35 = UIHelper.CreatePanel(val4.transform, "MissingEnemiesMessage", new Vector2(780f, 40f)); if ((Object)(object)val35 != (Object)null) { RectTransform component17 = val35.GetComponent<RectTransform>(); component17.anchorMin = new Vector2(0.5f, 0f); component17.anchorMax = new Vector2(0.5f, 0f); component17.pivot = new Vector2(0.5f, 0f); component17.anchoredPosition = new Vector2(0f, 10f); Image component18 = val35.GetComponent<Image>(); if ((Object)(object)component18 != (Object)null) { ((Graphic)component18).color = new Color(0.2f, 0.2f, 0.2f, 0.8f); } GameObject val36 = UIHelper.CreateText(val35.transform, "Text", "If enemies are missing, start a round to generate their entries.", (TextAlignmentOptions)514); if ((Object)(object)val36 != (Object)null) { TextMeshProUGUI component19 = val36.GetComponent<TextMeshProUGUI>(); if ((Object)(object)component19 != (Object)null) { ((TMP_Text)component19).fontSize = 14f; ((Graphic)component19).color = new Color(1f, 0.9f, 0.5f, 1f); } } } EnemyImageLoader.Initialize(); Plugin.LogInfo("Config menu created successfully"); } catch (Exception ex) { Plugin.LogError("Error creating menu prefab: " + ex.Message + "\nStack trace: " + ex.StackTrace); } } private void ToggleGlobalSettings() { isShowingGlobalSettings = !isShowingGlobalSettings; if (isShowingGlobalSettings) { ShowGlobalSettings(); UpdateGlobalSettingsButtonStyle(isActive: true); } else { selectedEnemyName = null; UpdateConfig