using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Joive")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Enemy spawn and stat controls for Burglin Gnomes.")] [assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyInformationalVersion("1.0.7")] [assembly: AssemblyProduct("BG Enemy Control")] [assembly: AssemblyTitle("BG-EnemyControl-Joive")] [assembly: AssemblyVersion("1.0.7.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BGEnemyControlJoive { [BepInPlugin("joive.bg.enemycontrol", "BG Enemy Control", "1.0.7")] public sealed class EnemyControlPlugin : BaseUnityPlugin { private sealed class EnemyRule { public string Name; public ConfigEntry Enabled; public ConfigEntry SpawnPercent; public ConfigEntry MaxAlive; public ConfigEntry HealthPercent; public ConfigEntry DamagePercent; public ConfigEntry SpeedPercent; public string SpawnText; public string MaxAliveText; public string HealthText; public string DamageText; public string SpeedText; } private const string Version = "1.0.7"; private static readonly string[] EnemyNames = new string[12] { "Redcap", "Rat", "Roach", "Cat", "Human", "Mole", "Vacuum", "Fairy", "Boar", "Pitbull", "Bibi", "Spider" }; private static readonly BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static FieldInfo runtimeConfigField; private static FieldInfo maxHealthField; private static FieldInfo aiAttackDamageField; private static FieldInfo aiAttackDealDamageField; private static ManualLogSource log; private readonly Dictionary rules = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary baseHealthByInstance = new Dictionary(); private readonly Dictionary baseSpeedByField = new Dictionary(); private readonly Dictionary baseDamageByAction = new Dictionary(); private readonly Dictionary liveCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly HashSet discoveredEnemies = new HashSet(StringComparer.OrdinalIgnoreCase); private Harmony harmony; private ConfigEntry enableMod; private ConfigEntry menuKey; private ConfigEntry debugHp; private ConfigEntry hostOnly; private ConfigEntry scanInterval; private ConfigEntry logChanges; private Rect windowRect = new Rect(80f, 80f, 720f, 520f); private Vector2 scroll; private bool showMenu; private bool centered; private bool modalActive; private bool oldCursorVisible; private CursorLockMode oldCursorLock; private PlayerController modalController; private bool hadModalControllerState; private bool oldControllerUiOpen; private int selectedTab; private float nextScanTime; private string lastAction = "Ready. Use Apply now after changing spawn values."; private int lastToggleFrame = -1; private int lastHealthApplications; private int lastSpeedApplications; private int lastDamageScales; private GUIStyle windowStyle; private GUIStyle titleStyle; private GUIStyle labelStyle; private GUIStyle smallStyle; private GUIStyle buttonStyle; private GUIStyle activeButtonStyle; private GUIStyle toggleStyle; private GUIStyle boxStyle; private GUIStyle textFieldStyle; private GUIStyle hpStyle; private Texture2D dimTexture; private Texture2D panelTexture; public static EnemyControlPlugin Instance { get; private set; } private void Awake() { Instance = this; log = ((BaseUnityPlugin)this).Logger; runtimeConfigField = AccessTools.Field(typeof(RuntimeEntityCollection), "myConfig"); maxHealthField = AccessTools.Field(typeof(HealthBase), "maxHealth"); enableMod = ((BaseUnityPlugin)this).Config.Bind("General", "EnableMod", true, "Enable enemy control."); menuKey = ((BaseUnityPlugin)this).Config.Bind("General", "MenuKey", "F8", "Menu key."); debugHp = ((BaseUnityPlugin)this).Config.Bind("Debug", "ShowPlayerHP", false, "Show local player HP at the bottom right."); hostOnly = ((BaseUnityPlugin)this).Config.Bind("General", "HostOnlyWorldChanges", true, "Only host/server changes enemy spawning and enemy stats."); scanInterval = ((BaseUnityPlugin)this).Config.Bind("General", "ScanInterval", 1f, "Seconds between enemy scans."); logChanges = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogChanges", false, "Write enemy control actions to the BepInEx log."); string[] enemyNames = EnemyNames; foreach (string enemyName in enemyNames) { RegisterEnemy(enemyName); } PatchGame(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("BG Enemy Control 1.0.7 loaded. Menu key: " + menuKey.Value)); } private void OnDestroy() { SetMenuVisible(visible: false); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(ParseKey(menuKey.Value, (KeyCode)289)) || Input.GetKeyDown((KeyCode)289) || Input.GetKeyDown((KeyCode)287) || Input.GetKeyDown((KeyCode)277)) { ToggleMenu("Update"); } if (enableMod.Value && Time.unscaledTime >= nextScanTime) { nextScanTime = Time.unscaledTime + Mathf.Max(0.25f, scanInterval.Value); ScanAndApply(); } } private void OnGUI() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 EnsureStyles(); Event current = Event.current; if (current != null && (int)current.type == 4 && (current.keyCode == ParseKey(menuKey.Value, (KeyCode)289) || (int)current.keyCode == 289 || (int)current.keyCode == 287 || (int)current.keyCode == 277)) { ToggleMenu("OnGUI"); current.Use(); } Color color = GUI.color; Color contentColor = GUI.contentColor; Color backgroundColor = GUI.backgroundColor; bool enabled = GUI.enabled; int depth = GUI.depth; GUI.color = Color.white; GUI.contentColor = Color.white; GUI.backgroundColor = Color.white; GUI.enabled = true; if (debugHp.Value) { DrawPlayerHp(); } if (!showMenu) { GUI.color = color; GUI.contentColor = contentColor; GUI.backgroundColor = backgroundColor; GUI.enabled = enabled; GUI.depth = depth; return; } KeepModalState(); GUI.depth = -9000; if ((Object)(object)dimTexture != (Object)null) { GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)dimTexture); } if (!centered) { centered = true; ((Rect)(ref windowRect)).width = Mathf.Min(760f, (float)Screen.width - 80f); ((Rect)(ref windowRect)).height = Mathf.Min(560f, (float)Screen.height - 80f); ((Rect)(ref windowRect)).x = ((float)Screen.width - ((Rect)(ref windowRect)).width) * 0.5f; ((Rect)(ref windowRect)).y = ((float)Screen.height - ((Rect)(ref windowRect)).height) * 0.5f; } windowRect = GUI.Window(44104, windowRect, new WindowFunction(DrawWindow), "", windowStyle); ((Rect)(ref windowRect)).x = Mathf.Clamp(((Rect)(ref windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref windowRect)).width)); ((Rect)(ref windowRect)).y = Mathf.Clamp(((Rect)(ref windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref windowRect)).height)); GUI.color = color; GUI.contentColor = contentColor; GUI.backgroundColor = backgroundColor; GUI.enabled = enabled; GUI.depth = depth; } private void PatchGame() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown harmony = new Harmony("joive.bg.enemycontrol"); Type typeFromHandle = typeof(RuntimeEntityCollection); Patch(typeFromHandle, "GetWantedCount", null, "GetWantedCountPostfix"); Patch(typeFromHandle, "GetCurrentSpawnChance", null, "GetCurrentSpawnChancePostfix"); Patch(typeFromHandle, "SpawnNew", "SpawnNewPrefix", null, Type.EmptyTypes); Patch(typeFromHandle, "SpawnNew", "SpawnNewPrefix", null, new Type[1] { typeof(Vector3) }); Patch(typeof(HealthBase), "Respawn", null, "HealthRespawnPostfix"); Patch(typeof(GameEntityBase), "OnReceiveDamage", "GameEntityDamagePrefix", null, new Type[1] { typeof(float) }); Type type = AccessTools.TypeByName("AiAttackAction"); if (type != null) { aiAttackDamageField = AccessTools.Field(type, "damage"); aiAttackDealDamageField = AccessTools.Field(type, "dealDamage"); Patch(type, "OnExecute", "AiAttackPrefix"); Patch(type, "OnUpdate", "AiAttackPrefix"); } } private void Patch(Type type, string methodName, string prefix = null, string postfix = null, Type[] parameters = null) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) try { MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameters, (Type[])null)); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch target not found: " + type.Name + "." + methodName)); return; } HarmonyMethod val = ((prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(EnemyControlPlugin).GetMethod(prefix, BindingFlags.Static | BindingFlags.Public))); HarmonyMethod val2 = ((postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(EnemyControlPlugin).GetMethod(postfix, BindingFlags.Static | BindingFlags.Public))); harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Patch failed: " + type.Name + "." + methodName + ": " + ex.Message)); } } private void SetMenuVisible(bool visible) { if (showMenu != visible) { showMenu = visible; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Enemy Control menu " + (showMenu ? "opened" : "closed") + ".")); if (showMenu) { centered = false; EnableModalMode(); } else { DisableModalMode(); } } } private void ToggleMenu(string source) { if (Time.frameCount != lastToggleFrame) { lastToggleFrame = Time.frameCount; SetMenuVisible(!showMenu); lastAction = "Menu toggled by " + source + "."; } } private void EnableModalMode() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!modalActive) { modalActive = true; oldCursorVisible = Cursor.visible; oldCursorLock = Cursor.lockState; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; SetGameplayUiOpen(value: true); } } private void DisableModalMode() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (modalActive) { modalActive = false; SetGameplayUiOpen(value: false); Cursor.visible = oldCursorVisible; Cursor.lockState = oldCursorLock; } } private void KeepModalState() { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; SetGameplayUiOpen(value: true); } private void SetGameplayUiOpen(bool value) { if (!value) { RestoreGameplayUiOpen(); return; } PlayerController val = Object.FindAnyObjectByType(); if (!((Object)(object)val == (Object)null)) { if ((Object)(object)modalController != (Object)(object)val) { RestoreGameplayUiOpen(); modalController = val; oldControllerUiOpen = GetControllerUiOpen(val); hadModalControllerState = true; } if (SetControllerUiOpen(val, value: true)) { val.RefreshMouseState(); } } } private void RestoreGameplayUiOpen() { if ((Object)(object)modalController == (Object)null || !hadModalControllerState) { modalController = null; hadModalControllerState = false; return; } if (SetControllerUiOpen(modalController, oldControllerUiOpen)) { modalController.RefreshMouseState(); } modalController = null; hadModalControllerState = false; } private static bool GetControllerUiOpen(PlayerController controller) { try { FieldInfo field = typeof(PlayerController).GetField("isUiOpen", AnyInstance); if (field != null) { return (bool)field.GetValue(controller); } } catch { } return false; } private static bool SetControllerUiOpen(PlayerController controller, bool value) { try { FieldInfo field = typeof(PlayerController).GetField("isUiOpen", AnyInstance); if (field == null) { return false; } field.SetValue(controller, value); return true; } catch { return false; } } private void FixOldConfigValues() { ResetNonRedcapStats(); EnemyRule rule = GetRule("Redcap"); if (rule != null) { rule.Enabled.Value = true; if (rule.SpawnPercent.Value <= 0f) { rule.SpawnPercent.Value = 35f; rule.SpawnText = "35"; } if (rule.MaxAlive.Value == 0) { rule.MaxAlive.Value = 2; rule.MaxAliveText = "2"; } } ((BaseUnityPlugin)this).Config.Save(); } private void ResetNonRedcapStats() { foreach (EnemyRule value in rules.Values) { if (!value.Name.Equals("Redcap", StringComparison.OrdinalIgnoreCase)) { value.Enabled.Value = true; value.HealthPercent.Value = 100f; value.DamagePercent.Value = 100f; value.SpeedPercent.Value = 100f; value.HealthText = "100"; value.DamageText = "100"; value.SpeedText = "100"; } } ((BaseUnityPlugin)this).Config.Save(); } private void RegisterEnemy(string enemyName) { if (!rules.ContainsKey(enemyName)) { bool flag = enemyName.Equals("Redcap", StringComparison.OrdinalIgnoreCase); EnemyRule value = new EnemyRule { Name = enemyName, Enabled = ((BaseUnityPlugin)this).Config.Bind(enemyName, "Enabled", true, "Allow this enemy to spawn and stay alive."), SpawnPercent = ((BaseUnityPlugin)this).Config.Bind(enemyName, "SpawnPercent", flag ? 35f : 100f, "Spawn percent. 0 disables new spawns, 100 is normal."), MaxAlive = ((BaseUnityPlugin)this).Config.Bind(enemyName, "MaxAlive", flag ? 2 : (-1), "Maximum alive enemies of this type. -1 means no cap."), HealthPercent = ((BaseUnityPlugin)this).Config.Bind(enemyName, "HealthPercent", flag ? 75f : 100f, "Enemy max health percent."), DamagePercent = ((BaseUnityPlugin)this).Config.Bind(enemyName, "DamagePercent", flag ? 60f : 100f, "Enemy attack damage percent."), SpeedPercent = ((BaseUnityPlugin)this).Config.Bind(enemyName, "SpeedPercent", flag ? 90f : 100f, "Enemy movement speed percent."), SpawnText = (flag ? 35f : 100f).ToString(CultureInfo.InvariantCulture), HealthText = (flag ? 75f : 100f).ToString(CultureInfo.InvariantCulture), DamageText = (flag ? 60f : 100f).ToString(CultureInfo.InvariantCulture), SpeedText = (flag ? 90f : 100f).ToString(CultureInfo.InvariantCulture), MaxAliveText = (flag ? 2 : (-1)).ToString(CultureInfo.InvariantCulture) }; rules[enemyName] = value; } } private void ScanAndApply() { try { DiscoverDirectorEnemies(); liveCounts.Clear(); GameEntityAI[] array = Object.FindObjectsByType((FindObjectsSortMode)0); GameEntityAI[] array2 = array; foreach (GameEntityAI val in array2) { if ((Object)(object)val == (Object)null) { continue; } string enemyName = GetEnemyName(val); if (!string.IsNullOrEmpty(enemyName)) { discoveredEnemies.Add(enemyName); if (!rules.ContainsKey(enemyName)) { RegisterEnemy(enemyName); } liveCounts.TryGetValue(enemyName, out var value); liveCounts[enemyName] = value + 1; if (CanChangeWorld()) { ApplyHealthTo(val); ApplySpeedTo(val); } } } if (CanChangeWorld()) { EnforceCaps(array); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Enemy scan failed: " + ex.Message)); } } private void ApplyAllNow() { lastHealthApplications = 0; lastSpeedApplications = 0; lastDamageScales = 0; ScanAndApply(); lastAction = "Applied now. Health changes: " + lastHealthApplications + ", speed changes: " + lastSpeedApplications + ". Damage scales when enemies hit."; } private void DiscoverDirectorEnemies() { AiDirector val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { return; } EnemySpawnConfig[] array = val.SpawnConfig ?? Array.Empty(); foreach (EnemySpawnConfig val2 in array) { string text = (((Object)(object)val2?.entityData != (Object)null) ? ((Object)val2.entityData).name : ""); if (!string.IsNullOrEmpty(text)) { discoveredEnemies.Add(text); RegisterEnemy(text); } } } private void EnforceCaps(GameEntityAI[] entities) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (GameEntityAI val in entities) { if ((Object)(object)val == (Object)null || ((GameEntityBase)val).IsDead) { continue; } string enemyName = GetEnemyName(val); if (!string.IsNullOrEmpty(enemyName)) { if (!dictionary.TryGetValue(enemyName, out var value)) { value = (dictionary[enemyName] = new List()); } value.Add(val); } } foreach (KeyValuePair> item in dictionary) { EnemyRule rule = GetRule(item.Key); if (rule == null) { continue; } int num = (rule.Enabled.Value ? rule.MaxAlive.Value : 0); if (num >= 0) { List list2 = item.Value.OrderBy((GameEntityAI e) => Vector3.SqrMagnitude(((Component)e).transform.position)).ToList(); for (int num2 = num; num2 < list2.Count; num2++) { DespawnEnemy(list2[num2], item.Key); } } } } private void DespawnEnemy(GameEntityAI entity, string name) { if ((Object)(object)entity == (Object)null) { return; } try { AiDirector val = Object.FindAnyObjectByType(); if ((Object)(object)val != (Object)null) { val.DespawnEnemy((GameEntityBase)(object)entity); } else if ((Object)(object)((NetworkBehaviour)entity).NetworkObject != (Object)null && ((NetworkBehaviour)entity).NetworkObject.IsSpawned) { ((NetworkBehaviour)entity).NetworkObject.Despawn(true); } else { Object.Destroy((Object)(object)((Component)entity).gameObject); } if (logChanges.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Despawning " + name + " due to Enemy Control cap.")); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Despawn failed for " + name + ": " + ex.Message)); } } private int DespawnByName(string enemyName) { if (!CanChangeWorld()) { lastAction = "Cannot despawn: you are not host/server."; return 0; } int num = 0; GameEntityAI[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (GameEntityAI val in array) { if (!((Object)(object)val == (Object)null) && !((GameEntityBase)val).IsDead && GetEnemyName(val).Equals(enemyName, StringComparison.OrdinalIgnoreCase)) { DespawnEnemy(val, enemyName); num++; } } ScanAndApply(); return num; } private int SpawnEnemyByName(string enemyName, int amount) { if (!CanChangeWorld()) { lastAction = "Cannot spawn: you are not host/server."; return 0; } AiDirector val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { lastAction = "Cannot spawn: AiDirector was not found on this scene."; return 0; } int num = 0; for (int i = 0; i < amount; i++) { try { val.SpawnEnemy(enemyName); num++; } catch (Exception ex) { lastAction = "Spawn failed for " + enemyName + ": " + ex.Message; break; } } ScanAndApply(); return num; } private void ApplyRedcapNow() { EnemyRule rule = GetRule("Redcap"); if (rule == null) { lastAction = "Redcap rule was not found."; return; } if (!CanChangeWorld()) { lastAction = "Cannot apply: you are not host/server."; return; } ScanAndApply(); if (!rule.Enabled.Value || rule.SpawnPercent.Value <= 0f || rule.MaxAlive.Value == 0) { lastAction = "Redcaps cleared now: " + DespawnByName("Redcap") + ". Future Redcaps are blocked."; return; } int liveCount = GetLiveCount("Redcap"); int num = ((rule.MaxAlive.Value > 0) ? rule.MaxAlive.Value : liveCount); int num2 = 0; if (rule.MaxAlive.Value > 0 && liveCount < num) { num2 = SpawnEnemyByName("Redcap", Mathf.Clamp(num - liveCount, 0, 30)); } else { ScanAndApply(); } lastAction = "Redcap applied: alive " + GetLiveCount("Redcap") + ((num > 0) ? ("/" + num) : "") + ", spawned " + num2 + "."; } private void ApplyHealthTo(GameEntityAI entity) { EnemyRule rule = GetRule(GetEnemyName(entity)); if (rule != null && !Mathf.Approximately(rule.HealthPercent.Value, 100f)) { HealthBase health = ((Component)entity).GetComponent() ?? ((Component)entity).GetComponentInChildren(true); ApplyHealthTo(health); } } private void ApplyHealthTo(HealthBase health) { if ((Object)(object)health == (Object)null || maxHealthField == null || !CanChangeWorld()) { return; } GameEntityAI val = ((Component)health).GetComponent() ?? ((Component)health).GetComponentInParent(); if ((Object)(object)val == (Object)null) { return; } EnemyRule rule = GetRule(GetEnemyName(val)); if (rule == null) { return; } int instanceID = ((Object)health).GetInstanceID(); if (!baseHealthByInstance.TryGetValue(instanceID, out var value)) { value = Convert.ToSingle(maxHealthField.GetValue(health), CultureInfo.InvariantCulture); baseHealthByInstance[instanceID] = Mathf.Max(1f, value); } float num = Mathf.Max(1f, value * Mathf.Max(0f, rule.HealthPercent.Value) / 100f); float num2 = Convert.ToSingle(maxHealthField.GetValue(health), CultureInfo.InvariantCulture); if (!Mathf.Approximately(num2, num)) { maxHealthField.SetValue(health, num); if (health.Health > num) { health.TakeDamage(health.Health - num); } else if (health.Health > 0f && health.Health >= num2 - 0.5f && num > health.Health) { health.Heal(num - health.Health); } lastHealthApplications++; } } private void ApplySpeedTo(GameEntityAI entity) { EnemyRule rule = GetRule(GetEnemyName(entity)); if (rule == null || Mathf.Approximately(rule.SpeedPercent.Value, 100f)) { return; } Component[] componentsInChildren = ((Component)entity).GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Type type = ((object)val).GetType(); string name = type.Name; if (name.IndexOf("Follower", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Movement", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Mecanim", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("AiLink", StringComparison.OrdinalIgnoreCase) < 0) { continue; } FieldInfo[] fields = type.GetFields(AnyInstance); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo.FieldType != typeof(float)) && LooksLikeSpeed(fieldInfo.Name)) { string key = ((Object)val).GetInstanceID() + ":" + fieldInfo.MetadataToken; float num = Convert.ToSingle(fieldInfo.GetValue(val), CultureInfo.InvariantCulture); if (!baseSpeedByField.TryGetValue(key, out var value)) { value = num; baseSpeedByField[key] = value; } float num2 = value * Mathf.Max(0f, rule.SpeedPercent.Value) / 100f; if (!float.IsNaN(num2) && !float.IsInfinity(num2) && !Mathf.Approximately(num, num2)) { fieldInfo.SetValue(val, num2); lastSpeedApplications++; } } } PropertyInfo[] properties = type.GetProperties(AnyInstance); foreach (PropertyInfo propertyInfo in properties) { if (!propertyInfo.CanRead || !propertyInfo.CanWrite || propertyInfo.PropertyType != typeof(float) || !LooksLikeSpeed(propertyInfo.Name)) { continue; } try { string key2 = ((Object)val).GetInstanceID() + ":p:" + propertyInfo.MetadataToken; float num3 = Convert.ToSingle(propertyInfo.GetValue(val, null), CultureInfo.InvariantCulture); if (!baseSpeedByField.TryGetValue(key2, out var value2)) { value2 = num3; baseSpeedByField[key2] = value2; } float num4 = value2 * Mathf.Max(0f, rule.SpeedPercent.Value) / 100f; if (!float.IsNaN(num4) && !float.IsInfinity(num4) && !Mathf.Approximately(num3, num4)) { propertyInfo.SetValue(val, num4, null); lastSpeedApplications++; } } catch { } } } } private static bool LooksLikeSpeed(string name) { string text = name.ToLowerInvariant(); switch (text) { default: if (!text.Contains("movespeed") && !text.Contains("movementspeed")) { return text.Contains("maxspeed"); } break; case "speed": case "maxspeed": case "naturalspeed": case "maxspeedatdistance": break; } return true; } private bool CanChangeWorld() { if (!hostOnly.Value) { return true; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsListening && !singleton.IsServer) { return singleton.IsHost; } return true; } private EnemyRule GetRule(string enemyName) { if (string.IsNullOrEmpty(enemyName)) { return null; } rules.TryGetValue(enemyName, out var value); return value; } private string GetEnemyName(GameEntityAI entity) { if ((Object)(object)entity == (Object)null) { return ""; } if ((Object)(object)((GameEntityBase)entity).EntityData != (Object)null && !string.IsNullOrEmpty(((Object)((GameEntityBase)entity).EntityData).name)) { return CleanName(((Object)((GameEntityBase)entity).EntityData).name); } Component[] components = ((Component)entity).GetComponents(); foreach (Component val in components) { if (!((Object)(object)val == (Object)null)) { string name = ((object)val).GetType().Name; if (!name.Equals("GameEntityAI", StringComparison.OrdinalIgnoreCase) && !name.Equals("NetworkObject", StringComparison.OrdinalIgnoreCase) && !name.Equals("Transform", StringComparison.OrdinalIgnoreCase)) { return CleanName(name); } } } return CleanName(((Object)((Component)entity).gameObject).name); } private static string CleanName(string value) { if (string.IsNullOrEmpty(value)) { return ""; } return value.Replace("(Clone)", "").Trim(); } private void DrawWindow(int id) { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("BG Enemy Control", titleStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Joive", smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); if (GUILayout.Button("X", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { SetMenuVisible(visible: false); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); TabButton("Main", 0); TabButton("Spawn", 1); TabButton("Stats", 2); TabButton("Debug", 3); GUILayout.EndHorizontal(); GUILayout.Space(8f); scroll = GUILayout.BeginScrollView(scroll, false, true, Array.Empty()); switch (selectedTab) { case 0: DrawMainTab(); break; case 1: DrawSpawnTab(); break; case 2: DrawStatsTab(); break; case 3: DrawDebugTab(); break; } GUILayout.EndScrollView(); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("BG Enemy Control by Joive", smallStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { SetMenuVisible(visible: false); } GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 28f)); GUILayout.EndVertical(); } private void DrawMainTab() { Toggle(enableMod, "Enable mod"); Toggle(debugHp, "Show player HP"); Toggle(hostOnly, "Host/server changes only"); Toggle(logChanges, "Log changes"); GUILayout.Label("Menu key: " + menuKey.Value, labelStyle, Array.Empty()); GUILayout.Label("Open keys: F8, F6, Insert", smallStyle, Array.Empty()); GUILayout.Label("World changes: " + (CanChangeWorld() ? "YES, this player can apply enemy changes." : "NO, only the host/server can change enemies."), labelStyle, Array.Empty()); GUILayout.Label("Last action: " + lastAction, smallStyle, Array.Empty()); GUILayout.Label("Spawn % changes future enemy checks. Apply now changes the current map.", smallStyle, Array.Empty()); GUILayout.Space(8f); DrawRedcapQuickControls(); GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Save config", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { ((BaseUnityPlugin)this).Config.Save(); lastAction = "Config saved."; } if (GUILayout.Button("Refresh counts", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { ScanAndApply(); lastAction = "Counts refreshed."; } if (GUILayout.Button("Fix old config", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { FixOldConfigValues(); ScanAndApply(); lastAction = "Old config values fixed."; } GUILayout.EndHorizontal(); DrawLiveCounts(); } private void DrawRedcapQuickControls() { EnemyRule rule = GetRule("Redcap"); if (rule == null) { return; } GUILayout.Label("Redcap quick control", titleStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); Toggle(rule.Enabled, "Redcap enabled", GUILayout.Width(150f)); PercentEditor(rule, rule.SpawnPercent, ref rule.SpawnText, "Spawn %", 5f, 0f, 300f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("No redcaps", buttonStyle, Array.Empty())) { rule.Enabled.Value = false; rule.SpawnPercent.Value = 0f; rule.SpawnText = "0"; rule.MaxAlive.Value = 0; rule.MaxAliveText = "0"; ApplyRedcapNow(); } if (GUILayout.Button("10%", buttonStyle, Array.Empty())) { rule.Enabled.Value = true; rule.SpawnPercent.Value = 10f; rule.SpawnText = "10"; rule.MaxAlive.Value = -1; rule.MaxAliveText = "-1"; lastAction = "Redcap future spawn chance set to 10%."; } if (GUILayout.Button("35%", buttonStyle, Array.Empty())) { rule.Enabled.Value = true; rule.SpawnPercent.Value = 35f; rule.SpawnText = "35"; rule.MaxAlive.Value = 2; rule.MaxAliveText = "2"; ApplyRedcapNow(); } if (GUILayout.Button("100%", buttonStyle, Array.Empty())) { rule.Enabled.Value = true; rule.SpawnPercent.Value = 100f; rule.SpawnText = "100"; rule.MaxAlive.Value = -1; rule.MaxAliveText = "-1"; lastAction = "Redcap future spawn chance set to normal."; } if (GUILayout.Button("10 alive", buttonStyle, Array.Empty())) { rule.Enabled.Value = true; rule.SpawnPercent.Value = 300f; rule.SpawnText = "300"; rule.MaxAlive.Value = 10; rule.MaxAliveText = "10"; ApplyRedcapNow(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Apply Redcap now", activeButtonStyle, Array.Empty())) { ApplyRedcapNow(); } if (GUILayout.Button("Spawn one Redcap", buttonStyle, Array.Empty())) { rule.Enabled.Value = true; if (rule.SpawnPercent.Value <= 0f) { rule.SpawnPercent.Value = 100f; rule.SpawnText = "100"; } int num = SpawnEnemyByName("Redcap", 1); lastAction = "Spawn one Redcap: spawned " + num + ". Alive now: " + GetLiveCount("Redcap") + "."; } if (GUILayout.Button("Clear Redcaps now", buttonStyle, Array.Empty())) { lastAction = "Redcaps removed now: " + DespawnByName("Redcap") + "."; } GUILayout.EndHorizontal(); GUILayout.Label("Use Apply Redcap now after changing Spawn % or Max alive. No redcaps clears current Redcaps and blocks new ones.", smallStyle, Array.Empty()); } private void DrawSpawnTab() { GUILayout.Label("Spawn control", titleStyle, Array.Empty()); foreach (EnemyRule item in OrderedRules()) { GUILayout.BeginVertical(boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); Toggle(item.Enabled, item.Name, GUILayout.Width(170f)); GUILayout.Label("Alive: " + GetLiveCount(item.Name), smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); PercentEditor(item, item.SpawnPercent, ref item.SpawnText, "Spawn %", 10f, 0f, 300f); IntEditor(item.MaxAlive, ref item.MaxAliveText, "Max alive", 1, -1, 99); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } GUILayout.Label("Max alive -1 means no cap. Disabled enemies are despawned by host/server.", smallStyle, Array.Empty()); } private void DrawStatsTab() { GUILayout.Label("Enemy stats", titleStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Apply now", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { ApplyAllNow(); } if (GUILayout.Button("Reset non-redcap stats", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) })) { ResetNonRedcapStats(); ScanAndApply(); lastAction = "Non-redcap stats reset to 100%."; } GUILayout.EndHorizontal(); foreach (EnemyRule item in OrderedRules()) { GUILayout.BeginVertical(boxStyle, Array.Empty()); GUILayout.Label(item.Name + " alive: " + GetLiveCount(item.Name), labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); PercentEditor(item, item.HealthPercent, ref item.HealthText, "Health %", 10f, 1f, 500f); PercentEditor(item, item.DamagePercent, ref item.DamageText, "Damage %", 10f, 0f, 500f); PercentEditor(item, item.SpeedPercent, ref item.SpeedText, "Speed %", 10f, 0f, 300f); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } GUILayout.Label("Health/speed apply to alive enemies now. Damage applies when an enemy actually hits something.", smallStyle, Array.Empty()); GUILayout.Label("Last damage scales this run: " + lastDamageScales, smallStyle, Array.Empty()); } private void DrawDebugTab() { GUILayout.Label("Debug", titleStyle, Array.Empty()); Toggle(debugHp, "Show player HP bottom right"); GUILayout.Label("World changes: " + (CanChangeWorld() ? "YES" : "NO - host/server only"), labelStyle, Array.Empty()); GUILayout.Label("Last action: " + lastAction, smallStyle, Array.Empty()); GUILayout.Label("Discovered enemies: " + string.Join(", ", (from r in OrderedRules() select r.Name).ToArray()), smallStyle, Array.Empty()); DrawLiveCounts(); } private void DrawLiveCounts() { GUILayout.Space(8f); GUILayout.Label("Alive enemies", titleStyle, Array.Empty()); foreach (EnemyRule item in OrderedRules()) { GUILayout.Label(item.Name + ": " + GetLiveCount(item.Name), smallStyle, Array.Empty()); } } private IEnumerable OrderedRules() { string[] enemyNames = EnemyNames; foreach (string key in enemyNames) { if (rules.TryGetValue(key, out var value)) { yield return value; } } foreach (string name in discoveredEnemies.OrderBy((string x) => x)) { if (!EnemyNames.Any((string x) => x.Equals(name, StringComparison.OrdinalIgnoreCase)) && rules.TryGetValue(name, out var value2)) { yield return value2; } } } private int GetLiveCount(string name) { liveCounts.TryGetValue(name, out var value); return value; } private void TabButton(string text, int tab) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (GUILayout.Button(text, (selectedTab == tab) ? activeButtonStyle : buttonStyle, Array.Empty())) { selectedTab = tab; scroll = Vector2.zero; } } private void Toggle(ConfigEntry entry, string text, params GUILayoutOption[] options) { bool flag = GUILayout.Toggle(entry.Value, text, toggleStyle, options); if (flag != entry.Value) { entry.Value = flag; } } private void PercentEditor(EnemyRule rule, ConfigEntry entry, ref string text, string label, float step, float min, float max) { GUILayout.Label(label, smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); if (GUILayout.Button("-", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { entry.Value = Mathf.Clamp(entry.Value - step, min, max); text = entry.Value.ToString("0.##", CultureInfo.InvariantCulture); } string text2 = GUILayout.TextField(text, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }); if (text2 != text) { text = text2; if (TryParseFloat(text, out var value)) { entry.Value = Mathf.Clamp(value, min, max); } } if (GUILayout.Button("+", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { entry.Value = Mathf.Clamp(entry.Value + step, min, max); text = entry.Value.ToString("0.##", CultureInfo.InvariantCulture); } } private void IntEditor(ConfigEntry entry, ref string text, string label, int step, int min, int max) { GUILayout.Label(label, smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); if (GUILayout.Button("-", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { entry.Value = Mathf.Clamp(entry.Value - step, min, max); text = entry.Value.ToString(CultureInfo.InvariantCulture); } string text2 = GUILayout.TextField(text, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) }); if (text2 != text) { text = text2; if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { entry.Value = Mathf.Clamp(result, min, max); } } if (GUILayout.Button("+", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { entry.Value = Mathf.Clamp(entry.Value + step, min, max); text = entry.Value.ToString(CultureInfo.InvariantCulture); } } private static bool TryParseFloat(string text, out float value) { return float.TryParse((text ?? "").Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out value); } private void DrawPlayerHp() { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) PlayerNetworking val = FindLocalPlayer(); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.Health == (Object)null)) { float num = ((maxHealthField != null) ? Convert.ToSingle(maxHealthField.GetValue(val.Health), CultureInfo.InvariantCulture) : 100f); string text = "HP " + ((HealthBase)val.Health).Health.ToString("0", CultureInfo.InvariantCulture) + " / " + num.ToString("0", CultureInfo.InvariantCulture); GUI.Label(new Rect((float)Screen.width - 170f, (float)Screen.height - 42f, 155f, 28f), text, hpStyle); } } private static PlayerNetworking FindLocalPlayer() { PlayerNetworking[] array = Object.FindObjectsByType((FindObjectsSortMode)0); PlayerNetworking[] array2 = array; foreach (PlayerNetworking val in array2) { if ((Object)(object)val != (Object)null && ((NetworkBehaviour)val).IsLocalPlayer) { return val; } } return ((IEnumerable)array).FirstOrDefault((Func)((PlayerNetworking p) => (Object)(object)p != (Object)null)); } private void EnsureStyles() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Expected O, but got Unknown //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Expected O, but got Unknown //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Expected O, but got Unknown //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Expected O, but got Unknown //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Expected O, but got Unknown //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Expected O, but got Unknown //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_042f: Unknown result type (might be due to invalid IL or missing references) //IL_0436: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_0452: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Expected O, but got Unknown //IL_0476: Unknown result type (might be due to invalid IL or missing references) if (windowStyle == null) { dimTexture = MakeTexture(new Color(0f, 0f, 0f, 0.12f)); panelTexture = MakeTexture(new Color(0.18f, 0.22f, 0.26f, 0.98f)); GUIStyle val = new GUIStyle(GUI.skin.window) { padding = new RectOffset(14, 14, 12, 12) }; val.normal.background = panelTexture; val.onNormal.background = panelTexture; windowStyle = val; titleStyle = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1 }; SetText(titleStyle, new Color(0.88f, 1f, 1f)); labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 13 }; SetText(labelStyle, Color.white); smallStyle = new GUIStyle(GUI.skin.label) { fontSize = 11 }; SetText(smallStyle, new Color(0.9f, 0.97f, 1f)); GUIStyle val2 = new GUIStyle(GUI.skin.button) { fontSize = 12 }; val2.normal.textColor = Color.white; val2.normal.background = MakeTexture(new Color(0.16f, 0.28f, 0.34f, 1f)); val2.hover.background = MakeTexture(new Color(0.2f, 0.35f, 0.42f, 1f)); val2.active.background = MakeTexture(new Color(0.05f, 0.56f, 0.7f, 1f)); buttonStyle = val2; SetText(buttonStyle, Color.white); GUIStyle val3 = new GUIStyle(buttonStyle); val3.normal.textColor = Color.white; val3.normal.background = MakeTexture(new Color(0.04f, 0.58f, 0.72f, 1f)); activeButtonStyle = val3; SetText(activeButtonStyle, Color.white); GUIStyle val4 = new GUIStyle(GUI.skin.toggle) { fontSize = 12 }; val4.normal.textColor = Color.white; val4.onNormal.textColor = new Color(0.7f, 1f, 0.88f); toggleStyle = val4; SetText(toggleStyle, Color.white); toggleStyle.onNormal.textColor = new Color(0.7f, 1f, 0.88f); toggleStyle.onHover.textColor = new Color(0.7f, 1f, 0.88f); toggleStyle.onActive.textColor = new Color(0.7f, 1f, 0.88f); toggleStyle.onFocused.textColor = new Color(0.7f, 1f, 0.88f); GUIStyle val5 = new GUIStyle(GUI.skin.box) { padding = new RectOffset(8, 8, 6, 6), margin = new RectOffset(2, 2, 3, 3) }; val5.normal.background = MakeTexture(new Color(0.14f, 0.17f, 0.2f, 0.94f)); boxStyle = val5; SetText(boxStyle, Color.white); GUIStyle val6 = new GUIStyle(GUI.skin.textField) { fontSize = 12 }; val6.normal.textColor = Color.white; val6.normal.background = MakeTexture(new Color(0.08f, 0.1f, 0.12f, 1f)); textFieldStyle = val6; SetText(textFieldStyle, Color.white); GUIStyle val7 = new GUIStyle(GUI.skin.label) { fontSize = 15, fontStyle = (FontStyle)1, alignment = (TextAnchor)5 }; val7.normal.textColor = new Color(0.8f, 1f, 0.88f); hpStyle = val7; SetText(hpStyle, new Color(0.8f, 1f, 0.88f)); } } private static void SetText(GUIStyle style, Color color) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) style.normal.textColor = color; style.hover.textColor = color; style.active.textColor = color; style.focused.textColor = color; style.onNormal.textColor = color; style.onHover.textColor = color; style.onActive.textColor = color; style.onFocused.textColor = color; } private static Texture2D MakeTexture(Color color) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } private static KeyCode ParseKey(string value, KeyCode fallback) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse(value, ignoreCase: true, out KeyCode result)) { return result; } return fallback; } public static void GetWantedCountPostfix(object __instance, ref int __result) { Instance?.ScaleWantedCount(__instance, ref __result); } public static void GetCurrentSpawnChancePostfix(object __instance, ref float __result) { Instance?.ScaleSpawnChance(__instance, ref __result); } public static bool SpawnNewPrefix(object __instance) { if (!((Object)(object)Instance == (Object)null)) { return Instance.AllowSpawn(__instance); } return true; } public static void HealthRespawnPostfix(HealthBase __instance) { Instance?.ApplyHealthTo(__instance); } public static void AiAttackPrefix(object __instance) { Instance?.ApplyAttackDamage(__instance); } public static void GameEntityDamagePrefix(GameEntityBase __instance, ref float damage) { Instance?.ScaleIncomingDamage(__instance, ref damage); } private void ScaleWantedCount(object runtimeCollection, ref int result) { if (!CanChangeWorld()) { return; } EnemyRule rule = GetRule(GetRuntimeEnemyName(runtimeCollection)); if (rule == null) { return; } if (!enableMod.Value || !rule.Enabled.Value || rule.SpawnPercent.Value <= 0f) { result = 0; return; } int num = result; result = Mathf.FloorToInt((float)num * Mathf.Max(0f, rule.SpawnPercent.Value) / 100f); if (num > 0 && rule.SpawnPercent.Value > 0f && result == 0) { result = 1; } if (rule.MaxAlive.Value > 0 && rule.SpawnPercent.Value >= 100f) { result = Mathf.Max(result, rule.MaxAlive.Value); } if (rule.MaxAlive.Value >= 0) { result = Mathf.Min(result, rule.MaxAlive.Value); } } private void ScaleSpawnChance(object runtimeCollection, ref float result) { if (!CanChangeWorld()) { return; } EnemyRule rule = GetRule(GetRuntimeEnemyName(runtimeCollection)); if (rule != null) { if (!enableMod.Value || !rule.Enabled.Value || rule.SpawnPercent.Value <= 0f) { result = 0f; } else { result = Mathf.Clamp01(result * Mathf.Max(0f, rule.SpawnPercent.Value) / 100f); } } } private bool AllowSpawn(object runtimeCollection) { if (!CanChangeWorld()) { return true; } if (!enableMod.Value) { return true; } EnemyRule rule = GetRule(GetRuntimeEnemyName(runtimeCollection)); if (rule == null) { return true; } if (rule.Enabled.Value) { return rule.SpawnPercent.Value > 0f; } return false; } private string GetRuntimeEnemyName(object runtimeCollection) { try { object? obj = runtimeConfigField?.GetValue(runtimeCollection); EnemySpawnConfig val = (EnemySpawnConfig)((obj is EnemySpawnConfig) ? obj : null); return ((Object)(object)val?.entityData != (Object)null) ? CleanName(((Object)val.entityData).name) : ""; } catch { return ""; } } private void ApplyAttackDamage(object action) { if (!enableMod.Value || !CanChangeWorld() || action == null || aiAttackDamageField == null || aiAttackDealDamageField == null || !(bool)aiAttackDealDamageField.GetValue(action)) { return; } GameEntityAI actionAgent = GetActionAgent(action); EnemyRule rule = GetRule(GetEnemyName(actionAgent)); if (rule != null) { int hashCode = RuntimeHelpers.GetHashCode(action); float num = Convert.ToSingle(aiAttackDamageField.GetValue(action), CultureInfo.InvariantCulture); if (!baseDamageByAction.TryGetValue(hashCode, out var value)) { value = num; baseDamageByAction[hashCode] = value; } float num2 = value * Mathf.Max(0f, rule.DamagePercent.Value) / 100f; if (!Mathf.Approximately(num, num2)) { aiAttackDamageField.SetValue(action, num2); } } } private void ScaleIncomingDamage(GameEntityBase target, ref float damage) { if (!enableMod.Value || !CanChangeWorld() || (Object)(object)target == (Object)null || damage <= 0f) { return; } GameEntityBase mostRecentHitter = target.MostRecentHitter; GameEntityAI val = (GameEntityAI)(object)((mostRecentHitter is GameEntityAI) ? mostRecentHitter : null); if (!((Object)(object)val == (Object)null)) { EnemyRule rule = GetRule(GetEnemyName(val)); if (rule != null && !Mathf.Approximately(rule.DamagePercent.Value, 100f)) { float num = damage; damage = Mathf.Max(0f, num * Mathf.Max(0f, rule.DamagePercent.Value) / 100f); lastDamageScales++; } } } private static GameEntityAI GetActionAgent(object action) { Type type = action.GetType(); while (type != null) { PropertyInfo property = type.GetProperty("agent", AnyInstance); if (property != null && typeof(GameEntityAI).IsAssignableFrom(property.PropertyType)) { object? value = property.GetValue(action, null); return (GameEntityAI)((value is GameEntityAI) ? value : null); } FieldInfo fieldInfo = type.GetField("agent", AnyInstance) ?? type.GetField("_agent", AnyInstance); if (fieldInfo != null && typeof(GameEntityAI).IsAssignableFrom(fieldInfo.FieldType)) { object? value2 = fieldInfo.GetValue(action); return (GameEntityAI)((value2 is GameEntityAI) ? value2 : null); } type = type.BaseType; } return null; } } }