Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HoldGround v1.2.0
HoldGround.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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 UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.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 HoldGround { internal sealed class ConfigurationManagerAttributes { public string Category; public string DispName; public int? Order; } internal enum Cause { Other, EnemyHit, Blocked, EnemyShield } [Flags] internal enum WeaponTypes { None = 0, Swords = 1, Axes = 2, Knives = 4, Spears = 8, Polearms = 0x10, Clubs = 0x20, Unarmed = 0x40, Pickaxes = 0x80, Bows = 0x100, Crossbows = 0x200, ElementalMagic = 0x400, BloodMagic = 0x800 } internal static class Knockback { internal struct State { internal Cause Current; internal WeaponTypes? YourArea; } internal static Cause Current; internal static WeaponTypes? YourArea; internal static HitData Blocked; internal static bool Deflecting; internal static State Save() { return new State { Current = Current, YourArea = YourArea }; } internal static void Restore(State state) { Current = state.Current; YourArea = state.YourArea; } internal static bool IsEnemy(Character character) { if ((Object)(object)character != (Object)null && !character.IsPlayer()) { return !character.IsTamed(); } return false; } internal static bool IsBlunt(DamageTypes damage) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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 (damage.m_blunt > 0f && damage.m_blunt >= damage.m_slash) { return damage.m_blunt >= damage.m_pierce; } return false; } internal static WeaponTypes TypeOf(SkillType skill, DamageTypes damage) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) switch (skill - 1) { case 0: return WeaponTypes.Swords; case 6: case 12: return WeaponTypes.Axes; case 1: return WeaponTypes.Knives; case 4: return WeaponTypes.Spears; case 3: return WeaponTypes.Polearms; case 2: return WeaponTypes.Clubs; case 10: return WeaponTypes.Unarmed; case 11: return WeaponTypes.Pickaxes; case 7: return WeaponTypes.Bows; case 13: return WeaponTypes.Crossbows; case 8: return WeaponTypes.ElementalMagic; case 9: return WeaponTypes.BloodMagic; default: if (!IsBlunt(damage)) { return WeaponTypes.None; } return WeaponTypes.Clubs; } } internal static Cause Of(HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)hit.m_hitType == 0 && !hit.HaveAttacker()) { return Cause.EnemyShield; } if (!IsEnemy(hit.GetAttacker())) { return Cause.Other; } if (hit != Blocked) { return Cause.EnemyHit; } return Cause.Blocked; } internal static float Share(ConfigEntry<int> percent) { return (float)percent.Value / 100f; } internal static float KeptOnYou(Cause cause) { return cause switch { Cause.EnemyHit => Share(Plugin.EnemyHits), Cause.Blocked => Share(Plugin.YourBlocks), Cause.EnemyShield => Share(Plugin.EnemyShields), _ => 1f, }; } internal static float KeptOnEnemy(WeaponTypes type) { if ((Plugin.KeepKnockback.Value & type) == 0) { return Share(Plugin.WeaponHits); } return 1f; } internal static string Percent(float share) { return $"{share * 100f:0}%"; } internal static string Name(WeaponTypes type) { if (type != WeaponTypes.None) { return type.ToString(); } return "no weapon type"; } } [HarmonyPatch(typeof(Character), "ApplyPushback", new Type[] { typeof(HitData) })] internal static class HitPushbackPatch { private static void Prefix(Character __instance, HitData hit, out Knockback.State __state) { __state = Knockback.Save(); Knockback.YourArea = null; Knockback.Current = Cause.Other; if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Knockback.Current = Knockback.Of(hit); Knockback.Blocked = null; } } private static void Finalizer(Knockback.State __state) { Knockback.Restore(__state); } } [HarmonyPatch(typeof(Aoe), "OnHit")] internal static class AoePatch { private static void Prefix(Character ___m_owner, SkillType ___m_skill, DamageTypes ___m_damage, out Knockback.State __state) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) __state = Knockback.Save(); Knockback.Current = (Knockback.IsEnemy(___m_owner) ? Cause.EnemyHit : Cause.Other); Knockback.YourArea = (((Object)(object)___m_owner != (Object)null && (Object)(object)___m_owner == (Object)(object)Player.m_localPlayer) ? new WeaponTypes?(Knockback.TypeOf(___m_skill, ___m_damage)) : ((WeaponTypes?)null)); } private static void Finalizer(Knockback.State __state) { Knockback.Restore(__state); } } [HarmonyPatch(typeof(Character), "ApplyPushback", new Type[] { typeof(Vector3), typeof(float) })] internal static class PushbackPatch { private static bool Prefix(Character __instance, ref float pushForce) { if (pushForce == 0f) { return true; } float num; if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { num = Knockback.KeptOnYou(Knockback.Current); if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)($"Pushback on you: force {pushForce:0.#}, cause {Knockback.Current}, " + Knockback.Percent(num) + " kept.")); } } else { if (!Knockback.YourArea.HasValue || !Knockback.IsEnemy(__instance)) { return true; } WeaponTypes value = Knockback.YourArea.Value; num = Knockback.KeptOnEnemy(value); if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)($"Area push from you on {((Object)__instance).name}: force {pushForce:0.#}, " + Knockback.Name(value) + ", " + Knockback.Percent(num) + " kept.")); } } if (num <= 0f) { return false; } pushForce *= num; return true; } } [HarmonyPatch(typeof(Character), "Damage")] internal static class WeaponHitPatch { private static void Prefix(Character __instance, HitData hit) { //IL_0024: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (hit.m_pushForce != 0f && Knockback.IsEnemy(__instance) && !((Object)(object)Player.m_localPlayer == (Object)null) && !(hit.m_attacker != ((Character)Player.m_localPlayer).GetZDOID())) { WeaponTypes type = Knockback.YourArea ?? Knockback.TypeOf(hit.m_skill, hit.m_damage); float num = Knockback.KeptOnEnemy(type); if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)($"Your hit on {((Object)__instance).name}: force {hit.m_pushForce:0.#}, " + Knockback.Name(type) + ", " + Knockback.Percent(num) + " kept.")); } hit.m_pushForce *= num; } } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] internal static class BlockAttackPatch { private static void Prefix(Humanoid __instance, Character attacker, out bool __state) { __state = Knockback.Deflecting; Knockback.Deflecting = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && Knockback.IsEnemy(attacker); } private static void Postfix(Humanoid __instance, HitData hit, bool __result) { if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Knockback.Blocked = hit; } } private static void Finalizer(bool __state) { Knockback.Deflecting = __state; } } [HarmonyPatch(typeof(ItemData), "GetDeflectionForce", new Type[] { })] internal static class DeflectionForcePatch { private static void Postfix(ref float __result) { if (Knockback.Deflecting) { float num = Knockback.Share(Plugin.Deflection); if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)($"Deflection on the enemy you blocked: force {__result:0.#} before the block " + "share, " + Knockback.Percent(num) + " kept.")); } __result *= num; } } } [BepInPlugin("vh.holdground", "HoldGround", "1.2.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "vh.holdground"; public const string Name = "HoldGround"; public const string Version = "1.2.0"; private const string OnYou = "1. Knockback on you"; private const string OnEnemies = "2. Knockback on enemies"; private const string Debug = "3. Debug"; private const string OldOnYou = "1. No knockback on you"; private const string OldOnEnemies = "2. No knockback on enemies"; private static readonly (string OldSection, string Section, string Key)[] Switches = new(string, string, string)[5] { ("1. No knockback on you", "1. Knockback on you", "EnemyHits"), ("1. No knockback on you", "1. Knockback on you", "BlocksAndParries"), ("1. No knockback on you", "1. Knockback on you", "EnemyShields"), ("2. No knockback on enemies", "2. Knockback on enemies", "WeaponHits"), ("2. No knockback on enemies", "2. Knockback on enemies", "BlocksAndParries") }; private const string Slider = " 0 removes the push and 100 is vanilla."; internal static ManualLogSource Log; internal static ConfigEntry<int> EnemyHits; internal static ConfigEntry<int> YourBlocks; internal static ConfigEntry<int> EnemyShields; internal static ConfigEntry<int> WeaponHits; internal static ConfigEntry<WeaponTypes> KeepKnockback; internal static ConfigEntry<int> Deflection; internal static ConfigEntry<bool> DebugLog; private Harmony _harmony; private FileSystemWatcher _watcher; private DateTime _lastReload; private static ConfigurationManagerAttributes Display(string category, string name, int order) { return new ConfigurationManagerAttributes { Category = category, DispName = name, Order = order }; } private ConfigEntry<int> Percent(string section, string key, string name, int order, string description) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind<int>(section, key, 0, new ConfigDescription(description + " 0 removes the push and 100 is vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1] { Display(section, name, order) })); } private void Awake() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; MigrateSwitches(); EnemyHits = Percent("1. Knockback on you", "EnemyHits", "Enemy hits (% kept)", 3, "How much of vanilla's push enemy hits give you: melee, projectiles and area attacks that you don't block, including hits on your back while blocking. Vanilla pushes you by the attack's force."); YourBlocks = Percent("1. Knockback on you", "BlocksAndParries", "Your blocks and parries (% kept)", 2, "How much of vanilla's push hits you block or parry give you, including the one that breaks your guard. Vanilla pushes you by the share of your block power the hit used (a parry, with its bonus block power, only a little) and a guard break by the full attack force."); EnemyShields = Percent("1. Knockback on you", "EnemyShields", "Enemy blocks (% kept)", 1, "How much of vanilla's push you get when an enemy blocks your melee attack with its shield, which is the shield's deflection force. The game doesn't say who blocked, so in PvP another player's block counts too."); WeaponHits = Percent("2. Knockback on enemies", "WeaponHits", "Your weapon hits (% kept)", 3, "How much of vanilla's push your attacks give enemies, for every weapon type not ticked in 'Weapons that keep their knockback'. Covers melee, arrows, bolts, thrown spears and staff blasts. Vanilla pushes the enemy by the attack's force."); KeepKnockback = ((BaseUnityPlugin)this).Config.Bind<WeaponTypes>("2. Knockback on enemies", "KeepKnockback", WeaponTypes.Clubs | WeaponTypes.Unarmed, new ConfigDescription("Weapon types whose hits keep their full vanilla knockback on enemies, whatever 'Your weapon hits' is set to. A hit counts by the skill of the weapon that made it: sledges and maces are Clubs, battleaxes Axes, atgeirs Polearms, fists Unarmed, staffs ElementalMagic or BloodMagic. A hit with none of these skills (some modded weapons and areas) counts as Clubs when blunt is its biggest physical damage, and otherwise follows 'Your weapon hits'. Tick none to let 'Your weapon hits' cover everything.", (AcceptableValueBase)null, new object[1] { Display("2. Knockback on enemies", "Weapons that keep their knockback", 2) })); Deflection = Percent("2. Knockback on enemies", "BlocksAndParries", "Your blocks and parries (% kept)", 1, "How much of vanilla's push blocking or parrying an enemy's melee attack gives it. Vanilla shoves it away by your shield's deflection force, a little less the more of your block power the hit used; at 0 it stays in reach for your counter, and a parry still staggers it. Ranged hits never deflect."); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Debug", "DebugLog", false, new ConfigDescription("Write every pushback on you to BepInEx\\LogOutput.log, with its force, what caused it and how much of it the mod kept, and every push your hits and blocks would give an enemy, with the weapon type. For troubleshooting; leave it off otherwise.", (AcceptableValueBase)null, new object[1] { Display("3. Debug", "Debug log", 1) })); _harmony = new Harmony("vh.holdground"); _harmony.PatchAll(); SetupWatcher(); Log.LogInfo((object)"Loaded."); } private void OnDestroy() { _watcher?.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void MigrateSwitches() { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown Dictionary<ConfigDefinition, string> dictionary; try { dictionary = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(((BaseUnityPlugin)this).Config, null) as Dictionary<ConfigDefinition, string>; } catch (Exception ex) { Log.LogWarning((object)("Couldn't read the 1.1.0 settings, so they start from the defaults: " + ex.Message)); return; } if (dictionary == null) { return; } int num = 0; (string, string, string)[] switches = Switches; for (int i = 0; i < switches.Length; i++) { (string, string, string) tuple = switches[i]; string item = tuple.Item1; string item2 = tuple.Item2; string item3 = tuple.Item3; ConfigDefinition key = new ConfigDefinition(item, item3); if (dictionary.TryGetValue(key, out var value)) { dictionary.Remove(key); ConfigDefinition key2 = new ConfigDefinition(item2, item3); if (!dictionary.ContainsKey(key2) && bool.TryParse(value.Trim(), out var result)) { dictionary[key2] = (result ? "0" : "100"); num++; } } } if (num > 0) { Log.LogInfo((object)$"Moved {num} settings from 1.1.0's switches to the new sliders."); } } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); _watcher.Changed += ReloadConfig; _watcher.Created += ReloadConfig; _watcher.Renamed += ReloadConfig; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReloadConfig(object sender, FileSystemEventArgs e) { if ((DateTime.Now - _lastReload).TotalSeconds < 1.0 || !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { return; } _lastReload = DateTime.Now; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)"Configuration reloaded."); } catch (Exception ex) { Log.LogError((object)("Error reloading configuration: " + ex.Message)); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; } } } }