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 InitialChildCount = AccessTools.FieldRefAccess("initialChildCount"); private static readonly FieldRef CurrentChildCount = AccessTools.FieldRefAccess("currentChildCount"); internal static int CountAliveShells(IEnemyComponent root) { if (((root != null) ? root.ChildComponents : null) == null) { return 0; } int num = 0; List 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 EnablePierceNerf; internal static ConfigEntry EnableExplosiveNerf; internal static ConfigEntry PierceShellPenalty; internal static ConfigEntry PierceMinMultiplier; internal static ConfigEntry ExplosiveShellPenalty; internal static ConfigEntry ExplosiveMinMultiplier; internal static ConfigEntry ExplosiveFullArmorMultiplier; internal static ConfigEntry ExplosiveOpenCoreBonus; internal static ConfigEntry 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("General", "EnablePierceNerf", true, "Scale IgnoreImmunity (armor-pierce) damage against cores still protected by shells."); EnableExplosiveNerf = _config.Bind("General", "EnableExplosiveNerf", true, "Scale explosive / mega-explosive damage against cores still protected by shells, and replace vanilla core explosive bonuses."); PierceShellPenalty = _config.Bind("Piercing", "ShellPenalty", 0.55f, "Higher = harsher pierce falloff per alive shell. Multiplier = 1 / (1 + penalty * shells)."); PierceMinMultiplier = _config.Bind("Piercing", "MinMultiplier", 0.25f, "Floor for pierce damage multiplier against armored cores (0–1)."); ExplosiveShellPenalty = _config.Bind("Explosives", "ShellPenalty", 0.75f, "Higher = harsher explosive falloff per alive shell. Multiplier = 1 / (1 + penalty * shells)."); ExplosiveMinMultiplier = _config.Bind("Explosives", "MinMultiplier", 0.15f, "Floor for explosive damage multiplier against armored cores (0–1)."); ExplosiveFullArmorMultiplier = _config.Bind("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("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("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) { } } }