Decompiled source of CoreProtectionRework v2.0.0

BepInEx/plugins/CoreProtectionRework.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0")]
[assembly: AssemblyProduct("CoreProtectionRework")]
[assembly: AssemblyTitle("CoreProtectionRework")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.0.0")]
[module: UnverifiableCode]
[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 CoreProtectionRework
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "CoreProtectionRework";

		public const string PLUGIN_NAME = "CoreProtectionRework";

		public const string PLUGIN_VERSION = "2.0.0";
	}
}
namespace CoreProtection
{
	internal static class ArmorUtility
	{
		private static readonly FieldRef<EnemyCore, int> InitialChildCount = AccessTools.FieldRefAccess<EnemyCore, int>("initialChildCount");

		private static readonly FieldRef<EnemyCore, int> CurrentChildCount = AccessTools.FieldRefAccess<EnemyCore, int>("currentChildCount");

		internal static int CountAliveShells(IEnemyComponent root)
		{
			if (((root != null) ? root.ChildComponents : null) == null)
			{
				return 0;
			}
			int num = 0;
			List<IEnemyComponent> childComponents = root.ChildComponents;
			for (int i = 0; i < childComponents.Count; i++)
			{
				IEnemyComponent val = childComponents[i];
				if (val != null)
				{
					EnemyShell val2 = (EnemyShell)(object)((val is EnemyShell) ? val : null);
					if (val2 != null && ((EnemyPart)val2).IsAlive)
					{
						num++;
					}
					num += CountAliveShells(val);
				}
			}
			return num;
		}

		internal static bool IsFullyArmored(EnemyCore core)
		{
			if ((Object)(object)core == (Object)null)
			{
				return false;
			}
			try
			{
				int num = InitialChildCount.Invoke(core);
				if (num <= 0)
				{
					return false;
				}
				return CurrentChildCount.Invoke(core) >= num;
			}
			catch
			{
				return CountAliveShells((IEnemyComponent)(object)core) > 0;
			}
		}

		internal static bool HasLostShell(EnemyCore core)
		{
			if ((Object)(object)core == (Object)null)
			{
				return false;
			}
			try
			{
				int num = InitialChildCount.Invoke(core);
				if (num <= 0)
				{
					return false;
				}
				return CurrentChildCount.Invoke(core) < num;
			}
			catch
			{
				return false;
			}
		}

		internal static float ShellFalloffMultiplier(int aliveShells, float penalty, float minMultiplier)
		{
			if (aliveShells <= 0)
			{
				return 1f;
			}
			float num = ConfigManager.Clamp01(minMultiplier);
			if (penalty <= 0f)
			{
				return 1f;
			}
			float num2 = 1f / (1f + penalty * (float)aliveShells);
			if (!(num2 < num))
			{
				return num2;
			}
			return num;
		}

		internal static float GetPierceMultiplier(int aliveShells)
		{
			return ShellFalloffMultiplier(aliveShells, ConfigManager.PierceShellPenalty.Value, ConfigManager.PierceMinMultiplier.Value);
		}

		internal static float GetExplosiveArmorMultiplier(int aliveShells)
		{
			return ShellFalloffMultiplier(aliveShells, ConfigManager.ExplosiveShellPenalty.Value, ConfigManager.ExplosiveMinMultiplier.Value);
		}

		internal static void ScaleDamage(ref DamageData data, float multiplier)
		{
			if (!(multiplier >= 0.999f))
			{
				if (multiplier < 0f)
				{
					multiplier = 0f;
				}
				data.damage *= multiplier;
				data.effectAmount *= multiplier;
			}
		}
	}
	internal static class ConfigManager
	{
		private const float DebounceSeconds = 0.25f;

		internal static ConfigEntry<bool> EnablePierceNerf;

		internal static ConfigEntry<bool> EnableExplosiveNerf;

		internal static ConfigEntry<float> PierceShellPenalty;

		internal static ConfigEntry<float> PierceMinMultiplier;

		internal static ConfigEntry<float> ExplosiveShellPenalty;

		internal static ConfigEntry<float> ExplosiveMinMultiplier;

		internal static ConfigEntry<float> ExplosiveFullArmorMultiplier;

		internal static ConfigEntry<float> ExplosiveOpenCoreBonus;

		internal static ConfigEntry<float> ExplosiveArmoredBonus;

		private static ConfigFile _config;

		private static ManualLogSource _logger;

		private static FileSystemWatcher _configWatcher;

		private static volatile bool _reloadPending;

		private static float _lastReloadTime;

		internal static void Initialize(ConfigFile configFile, ManualLogSource log)
		{
			_config = configFile;
			_logger = log;
			EnablePierceNerf = _config.Bind<bool>("General", "EnablePierceNerf", true, "Scale IgnoreImmunity (armor-pierce) damage against cores still protected by shells.");
			EnableExplosiveNerf = _config.Bind<bool>("General", "EnableExplosiveNerf", true, "Scale explosive / mega-explosive damage against cores still protected by shells, and replace vanilla core explosive bonuses.");
			PierceShellPenalty = _config.Bind<float>("Piercing", "ShellPenalty", 0.55f, "Higher = harsher pierce falloff per alive shell. Multiplier = 1 / (1 + penalty * shells).");
			PierceMinMultiplier = _config.Bind<float>("Piercing", "MinMultiplier", 0.25f, "Floor for pierce damage multiplier against armored cores (0–1).");
			ExplosiveShellPenalty = _config.Bind<float>("Explosives", "ShellPenalty", 0.75f, "Higher = harsher explosive falloff per alive shell. Multiplier = 1 / (1 + penalty * shells).");
			ExplosiveMinMultiplier = _config.Bind<float>("Explosives", "MinMultiplier", 0.15f, "Floor for explosive damage multiplier against armored cores (0–1).");
			ExplosiveFullArmorMultiplier = _config.Bind<float>("Explosives", "FullArmorMultiplier", 0.1f, "Extra multiplier when no shells have been broken yet (vanilla free core hits on Grunt/Brute). 0 blocks effective damage.");
			ExplosiveOpenCoreBonus = _config.Bind<float>("Explosives", "OpenCoreBonus", 1f, "Replaces vanilla x2 explosive bonus once the core is fully exposed (0 shells left). 1 = no bonus, 2 = vanilla.");
			ExplosiveArmoredBonus = _config.Bind<float>("Explosives", "ArmoredBonus", 1f, "Replaces vanilla x2 explosive bonus while some shells remain but the core is already vulnerable. Applied after shell falloff.");
			try
			{
				SetupFileWatcher();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
				}
			}
		}

		internal static void Tick()
		{
			if (!_reloadPending || Time.unscaledTime - _lastReloadTime < 0.25f)
			{
				return;
			}
			_reloadPending = false;
			_lastReloadTime = Time.unscaledTime;
			try
			{
				_config.Reload();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogError((object)("Error reloading config: " + ex.Message));
				}
			}
		}

		internal static void Dispose()
		{
			if (_configWatcher != null)
			{
				_configWatcher.EnableRaisingEvents = false;
				_configWatcher.Changed -= OnConfigFileChanged;
				_configWatcher.Created -= OnConfigFileChanged;
				_configWatcher.Renamed -= OnConfigFileChanged;
				_configWatcher.Dispose();
				_configWatcher = null;
			}
			_config = null;
			_logger = null;
		}

		internal static float Clamp01(float value)
		{
			if (value < 0f)
			{
				return 0f;
			}
			if (value > 1f)
			{
				return 1f;
			}
			return value;
		}

		private static void SetupFileWatcher()
		{
			_configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.coreprotectionrework.cfg");
			_configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
			_configWatcher.Changed += OnConfigFileChanged;
			_configWatcher.Created += OnConfigFileChanged;
			_configWatcher.Renamed += OnConfigFileChanged;
			_configWatcher.EnableRaisingEvents = true;
		}

		private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
		{
			_reloadPending = true;
		}
	}
	[HarmonyPatch(typeof(EnemyCore), "Damage")]
	internal static class CoreDamagePatch
	{
		[HarmonyPrefix]
		private static void Prefix(EnemyCore __instance, ref DamageData data)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			bool value = ConfigManager.EnablePierceNerf.Value;
			bool value2 = ConfigManager.EnableExplosiveNerf.Value;
			if (!value && !value2)
			{
				return;
			}
			bool flag = DamageTypeUtility.IsPiercing(in data);
			bool flag2 = DamageTypeUtility.IsExplosiveStyle(in data);
			if (!flag && !flag2)
			{
				return;
			}
			int num = ArmorUtility.CountAliveShells((IEnemyComponent)(object)__instance);
			bool flag3 = ArmorUtility.IsFullyArmored(__instance);
			bool flag4 = ArmorUtility.HasLostShell(__instance);
			bool flag5 = DamageTypeUtility.VanillaAppliesExplosiveCoreBonus(in data) && (flag || flag4);
			if (flag && value && num > 0)
			{
				ArmorUtility.ScaleDamage(ref data, ArmorUtility.GetPierceMultiplier(num));
			}
			if (!flag2 || !value2)
			{
				return;
			}
			if (num <= 0)
			{
				if (flag5)
				{
					float num2 = Mathf.Max(0f, ConfigManager.ExplosiveOpenCoreBonus.Value);
					ArmorUtility.ScaleDamage(ref data, num2 * 0.5f);
				}
				return;
			}
			float explosiveArmorMultiplier = ArmorUtility.GetExplosiveArmorMultiplier(num);
			float num3 = Mathf.Max(0f, ConfigManager.ExplosiveArmoredBonus.Value);
			if (flag3 && !flag)
			{
				float num4 = Mathf.Max(0f, ConfigManager.ExplosiveFullArmorMultiplier.Value);
				ArmorUtility.ScaleDamage(ref data, explosiveArmorMultiplier * num4);
				return;
			}
			float num5 = explosiveArmorMultiplier;
			if (flag5)
			{
				num5 *= num3 * 0.5f;
			}
			ArmorUtility.ScaleDamage(ref data, num5);
		}
	}
	internal static class DamageTypeUtility
	{
		internal static bool IsPiercing(in DamageData data)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			return (int)data.effect == -1;
		}

		internal static bool IsExplosiveStyle(in DamageData data)
		{
			//IL_0001: 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)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			if ((data.damageFlags & 0x110) == 0)
			{
				return (int)data.effect == -4;
			}
			return true;
		}

		internal static bool VanillaAppliesExplosiveCoreBonus(in DamageData data)
		{
			//IL_0001: 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)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			if ((data.damageFlags & 0x100) == 0)
			{
				return (int)data.effect == -4;
			}
			return true;
		}
	}
	[BepInPlugin("sparroh.coreprotectionrework", "CoreProtectionRework", "2.0.0")]
	[MycoMod(/*Could not decode attribute arguments.*/)]
	public class CoreProtectionPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "sparroh.coreprotectionrework";

		public const string PluginName = "CoreProtectionRework";

		public const string PluginVersion = "2.0.0";

		internal static ManualLogSource Logger;

		private Harmony _harmony;

		internal static CoreProtectionPlugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger);
			_harmony = new Harmony("sparroh.coreprotectionrework");
			_harmony.PatchAll(typeof(CoreDamagePatch).Assembly);
			Logger.LogInfo((object)"CoreProtectionRework v2.0.0 loaded.");
			Logger.LogInfo((object)($"Pierce nerf: {ConfigManager.EnablePierceNerf.Value}, " + $"Explosive nerf: {ConfigManager.EnableExplosiveNerf.Value}"));
		}

		private void Update()
		{
			ConfigManager.Tick();
		}

		private void OnDestroy()
		{
			ConfigManager.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}