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)
{
}
}
}