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<bool> Enabled;
public ConfigEntry<float> SpawnPercent;
public ConfigEntry<int> MaxAlive;
public ConfigEntry<float> HealthPercent;
public ConfigEntry<float> DamagePercent;
public ConfigEntry<float> 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<string, EnemyRule> rules = new Dictionary<string, EnemyRule>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<int, float> baseHealthByInstance = new Dictionary<int, float>();
private readonly Dictionary<string, float> baseSpeedByField = new Dictionary<string, float>();
private readonly Dictionary<int, float> baseDamageByAction = new Dictionary<int, float>();
private readonly Dictionary<string, int> liveCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> discoveredEnemies = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private Harmony harmony;
private ConfigEntry<bool> enableMod;
private ConfigEntry<string> menuKey;
private ConfigEntry<bool> debugHp;
private ConfigEntry<bool> hostOnly;
private ConfigEntry<float> scanInterval;
private ConfigEntry<bool> 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<bool>("General", "EnableMod", true, "Enable enemy control.");
menuKey = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MenuKey", "F8", "Menu key.");
debugHp = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ShowPlayerHP", false, "Show local player HP at the bottom right.");
hostOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HostOnlyWorldChanges", true, "Only host/server changes enemy spawning and enemy stats.");
scanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScanInterval", 1f, "Seconds between enemy scans.");
logChanges = ((BaseUnityPlugin)this).Config.Bind<bool>("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<PlayerController>();
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<bool>(enemyName, "Enabled", true, "Allow this enemy to spawn and stay alive."),
SpawnPercent = ((BaseUnityPlugin)this).Config.Bind<float>(enemyName, "SpawnPercent", flag ? 35f : 100f, "Spawn percent. 0 disables new spawns, 100 is normal."),
MaxAlive = ((BaseUnityPlugin)this).Config.Bind<int>(enemyName, "MaxAlive", flag ? 2 : (-1), "Maximum alive enemies of this type. -1 means no cap."),
HealthPercent = ((BaseUnityPlugin)this).Config.Bind<float>(enemyName, "HealthPercent", flag ? 75f : 100f, "Enemy max health percent."),
DamagePercent = ((BaseUnityPlugin)this).Config.Bind<float>(enemyName, "DamagePercent", flag ? 60f : 100f, "Enemy attack damage percent."),
SpeedPercent = ((BaseUnityPlugin)this).Config.Bind<float>(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<GameEntityAI>((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<AiDirector>();
if ((Object)(object)val == (Object)null)
{
return;
}
EnemySpawnConfig[] array = val.SpawnConfig ?? Array.Empty<EnemySpawnConfig>();
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<string, List<GameEntityAI>> dictionary = new Dictionary<string, List<GameEntityAI>>(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<GameEntityAI>());
}
value.Add(val);
}
}
foreach (KeyValuePair<string, List<GameEntityAI>> 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<GameEntityAI> 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<AiDirector>();
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<GameEntityAI>((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<AiDirector>();
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<HealthBase>() ?? ((Component)entity).GetComponentInChildren<HealthBase>(true);
ApplyHealthTo(health);
}
}
private void ApplyHealthTo(HealthBase health)
{
if ((Object)(object)health == (Object)null || maxHealthField == null || !CanChangeWorld())
{
return;
}
GameEntityAI val = ((Component)health).GetComponent<GameEntityAI>() ?? ((Component)health).GetComponentInParent<GameEntityAI>();
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<Component>(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<Component>();
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<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUILayout.Label("BG Enemy Control", titleStyle, Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
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<GUILayoutOption>());
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<GUILayoutOption>());
GUILayout.Label("BG Enemy Control by Joive", smallStyle, Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
GUILayout.Label("Open keys: F8, F6, Insert", smallStyle, Array.Empty<GUILayoutOption>());
GUILayout.Label("World changes: " + (CanChangeWorld() ? "YES, this player can apply enemy changes." : "NO, only the host/server can change enemies."), labelStyle, Array.Empty<GUILayoutOption>());
GUILayout.Label("Last action: " + lastAction, smallStyle, Array.Empty<GUILayoutOption>());
GUILayout.Label("Spawn % changes future enemy checks. Apply now changes the current map.", smallStyle, Array.Empty<GUILayoutOption>());
GUILayout.Space(8f);
DrawRedcapQuickControls();
GUILayout.Space(8f);
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
if (GUILayout.Button("No redcaps", buttonStyle, Array.Empty<GUILayoutOption>()))
{
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<GUILayoutOption>()))
{
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<GUILayoutOption>()))
{
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<GUILayoutOption>()))
{
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<GUILayoutOption>()))
{
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<GUILayoutOption>());
if (GUILayout.Button("Apply Redcap now", activeButtonStyle, Array.Empty<GUILayoutOption>()))
{
ApplyRedcapNow();
}
if (GUILayout.Button("Spawn one Redcap", buttonStyle, Array.Empty<GUILayoutOption>()))
{
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<GUILayoutOption>()))
{
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<GUILayoutOption>());
}
private void DrawSpawnTab()
{
GUILayout.Label("Spawn control", titleStyle, Array.Empty<GUILayoutOption>());
foreach (EnemyRule item in OrderedRules())
{
GUILayout.BeginVertical(boxStyle, Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
}
private void DrawStatsTab()
{
GUILayout.Label("Enemy stats", titleStyle, Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
GUILayout.Label(item.Name + " alive: " + GetLiveCount(item.Name), labelStyle, Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
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<GUILayoutOption>());
GUILayout.Label("Last damage scales this run: " + lastDamageScales, smallStyle, Array.Empty<GUILayoutOption>());
}
private void DrawDebugTab()
{
GUILayout.Label("Debug", titleStyle, Array.Empty<GUILayoutOption>());
Toggle(debugHp, "Show player HP bottom right");
GUILayout.Label("World changes: " + (CanChangeWorld() ? "YES" : "NO - host/server only"), labelStyle, Array.Empty<GUILayoutOption>());
GUILayout.Label("Last action: " + lastAction, smallStyle, Array.Empty<GUILayoutOption>());
GUILayout.Label("Discovered enemies: " + string.Join(", ", (from r in OrderedRules()
select r.Name).ToArray()), smallStyle, Array.Empty<GUILayoutOption>());
DrawLiveCounts();
}
private void DrawLiveCounts()
{
GUILayout.Space(8f);
GUILayout.Label("Alive enemies", titleStyle, Array.Empty<GUILayoutOption>());
foreach (EnemyRule item in OrderedRules())
{
GUILayout.Label(item.Name + ": " + GetLiveCount(item.Name), smallStyle, Array.Empty<GUILayoutOption>());
}
}
private IEnumerable<EnemyRule> 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<GUILayoutOption>()))
{
selectedTab = tab;
scroll = Vector2.zero;
}
}
private void Toggle(ConfigEntry<bool> 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<float> 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<int> 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<PlayerNetworking>((FindObjectsSortMode)0);
PlayerNetworking[] array2 = array;
foreach (PlayerNetworking val in array2)
{
if ((Object)(object)val != (Object)null && ((NetworkBehaviour)val).IsLocalPlayer)
{
return val;
}
}
return ((IEnumerable<PlayerNetworking>)array).FirstOrDefault((Func<PlayerNetworking, bool>)((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<KeyCode>(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;
}
}
}