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 ImmersiveCombat v0.5.3
plugins/ImmersiveCombat/ImmersiveCombat.dll
Decompiled a month agousing System; using System.Diagnostics; using System.Linq; 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 ImmersiveCombat.Common; using ImmersiveCombat.Config; using ImmersiveCombat.Features.AttackCancel; using ImmersiveCombat.Features.HoldAttack; 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: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("ImmersiveCombat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.2.0")] [assembly: AssemblyInformationalVersion("0.5.2+4cba1c5655706eff9e6ad677cccff9b72fe6cd3c")] [assembly: AssemblyProduct("ImmersiveCombat")] [assembly: AssemblyTitle("ImmersiveCombat")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.5.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 ImmersiveCombat { [BepInPlugin("com.lubertmiliutin.immersivecombat", "ImmersiveCombat", "0.5.3")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.lubertmiliutin.immersivecombat"; public const string PluginName = "ImmersiveCombat"; public const string PluginVersion = "0.5.3"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.lubertmiliutin.immersivecombat"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); int num = 0; foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods()) { Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod); if (patchInfo != null && (patchInfo.Prefixes.Any((Patch p) => p.owner == "com.lubertmiliutin.immersivecombat") || patchInfo.Postfixes.Any((Patch p) => p.owner == "com.lubertmiliutin.immersivecombat") || patchInfo.Transpilers.Any((Patch p) => p.owner == "com.lubertmiliutin.immersivecombat"))) { num++; Log.LogInfo((object)(" [patch] " + allPatchedMethod.DeclaringType?.FullName + "::" + allPatchedMethod.Name)); } } Log.LogInfo((object)string.Format("{0} v{1} loaded with {2} patched method(s).", "ImmersiveCombat", "0.5.3", num)); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace ImmersiveCombat.Features.QuickDodge { [HarmonyPatch(typeof(Player), "Update")] internal static class QuickDodgePatch { [HarmonyPrefix] private static void Prefix(Player __instance) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.QuickDodgeEnabled.Value && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !IsTextInputActive() && Input.GetKeyDown(ModConfig.QuickDodgeKey.Value) && !((Character)__instance).IsDead() && !((Character)__instance).IsStaggering() && !((Character)__instance).InCutscene() && !((Character)__instance).InDodge() && __instance.GetDoodadController() == null && !HoldAttackState.IsActive) { if (((Character)__instance).InAttack() && ((Humanoid)__instance).m_currentAttack != null) { AttackCancelLogic.CancelAttack(__instance, ((Humanoid)__instance).m_currentAttack, refundStamina: false); } Vector3 val = ((Character)__instance).m_moveDir; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = -((Component)__instance).transform.forward; } else { ((Vector3)(ref val)).Normalize(); } QuickDodgeState.HalfStaminaActive = true; QuickDodgeState.IframeStartPending = true; __instance.Dodge(val * ModConfig.QuickDodgeDistanceScale.Value); if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)($"[QuickDodge] queued. dir=({val.x:F2},{val.z:F2}) " + $"scale={ModConfig.QuickDodgeDistanceScale.Value:F2} " + $"staminaFrac={ModConfig.QuickDodgeStaminaFraction.Value:F2}")); } } } [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (QuickDodgeState.HalfStaminaActive && (((Character)__instance).InDodge() || __instance.m_queuedDodgeTimer <= 0f)) { QuickDodgeState.HalfStaminaActive = false; } if (QuickDodgeState.IframeStartPending && __instance.m_dodgeInvincible) { QuickDodgeState.IframeStartPending = false; QuickDodgeState.IframeEndTime = Time.time + ModConfig.QuickDodgeIframeSeconds.Value; QuickDodgeState.ScaleRootMotion = true; if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)($"[QuickDodge] iframes armed for {ModConfig.QuickDodgeIframeSeconds.Value:F2}s, " + $"root-motion scaling = {ModConfig.QuickDodgeDistanceScale.Value:F2}")); } } if (QuickDodgeState.ScaleRootMotion && ((Character)__instance).InDodge() && (Object)(object)((Character)__instance).m_animator != (Object)null) { float num = Mathf.Max(0.05f, ModConfig.QuickDodgeDistanceScale.Value); ((Character)__instance).m_animator.speed = 1f / num; } if (QuickDodgeState.ScaleRootMotion && !((Character)__instance).InDodge()) { QuickDodgeState.ScaleRootMotion = false; if ((Object)(object)((Character)__instance).m_animator != (Object)null) { ((Character)__instance).m_animator.speed = 1f; } if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)"[QuickDodge] root-motion scaling disengaged (dodge anim ended)"); } } if (QuickDodgeState.IframeEndTime > 0f && Time.time >= QuickDodgeState.IframeEndTime) { if (__instance.m_dodgeInvincible) { __instance.OnDodgeMortal(); } QuickDodgeState.IframeEndTime = 0f; } } private static bool IsTextInputActive() { if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if (Console.IsVisible()) { return true; } if (TextInput.IsVisible()) { return true; } return false; } } [HarmonyPatch(typeof(Character), "AddRootMotion")] internal static class QuickDodgeRootMotionScalePatch { [HarmonyPrefix] private static void Prefix(Character __instance, ref Vector3 vel) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (QuickDodgeState.ScaleRootMotion && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { vel *= ModConfig.QuickDodgeDistanceScale.Value; } } } [HarmonyPatch(typeof(Player), "GetDodgeStaminaUse")] internal static class QuickDodgeStaminaShimPatch { [HarmonyPostfix] private static void Postfix(ref float __result) { if (QuickDodgeState.HalfStaminaActive) { __result *= ModConfig.QuickDodgeStaminaFraction.Value; } } } internal static class QuickDodgeState { public static bool HalfStaminaActive; public static bool IframeStartPending; public static float IframeEndTime; public static bool ScaleRootMotion; public static void Reset() { HalfStaminaActive = false; IframeStartPending = false; IframeEndTime = 0f; ScaleRootMotion = false; } } } namespace ImmersiveCombat.Features.HoldAttack { internal static class HoldAttackHelpers { public static bool IsProjectile(Attack a) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if ((int)a.m_attackType != 2 && (int)a.m_attackType != 5) { return (Object)(object)a.m_attackProjectile != (Object)null; } return true; } public static bool IsLocalPlayer(Humanoid humanoid) { if ((Object)(object)humanoid != (Object)null) { return (Object)(object)humanoid == (Object)(object)Player.m_localPlayer; } return false; } public static CharacterAnimEvent GetAnimEvent(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { return ((Component)humanoid).GetComponentInChildren<CharacterAnimEvent>(); } return null; } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class HoldAttackInputLockPatch { [HarmonyPrefix] private static void Prefix(Player __instance, ref Vector3 movedir, ref bool jump, ref bool dodge, ref bool run, ref bool autoRun) { //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 (ModConfig.HoldEnabled.Value && ModConfig.HoldLockMovement.Value && HoldAttackState.IsActive && HoldAttackHelpers.IsLocalPlayer((Humanoid)(object)__instance)) { movedir = Vector3.zero; jump = false; dodge = false; run = false; autoRun = false; } } } [HarmonyPatch(typeof(Player), "Update")] internal static class HoldAttackReleasePatch { [HarmonyPostfix] private static void Postfix(Player __instance) { HoldAttackState.TriggerReentryGuard = false; if (!HoldAttackState.IsActive || !HoldAttackHelpers.IsLocalPlayer((Humanoid)(object)__instance)) { return; } if (((Humanoid)__instance).m_currentAttack != HoldAttackState.PendingAttack || ((Character)__instance).IsDead() || ((Character)__instance).IsStaggering() || ((Character)__instance).InCutscene()) { if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)"[Hold] safety bailout — clearing deferred attack"); } HoldAttackState.Bailout(); return; } bool flag = ValheimButtons.AttackHeld(((Humanoid)__instance).m_currentAttackIsSecondary); bool flag2 = Time.time - HoldAttackState.HoldStartTime >= ModConfig.HoldMaxSeconds.Value; if (ModConfig.HoldDrainStamina.Value && flag && !flag2) { float num = ModConfig.HoldDrainStaminaPerSecond.Value * Time.deltaTime; if (num > 0f) { ((Character)__instance).UseStamina(num); if (__instance.GetStamina() <= 0f) { flag2 = true; } } } if (!flag || flag2) { Release(flag2); } } private static void Release(bool forced) { Attack pendingAttack = HoldAttackState.PendingAttack; bool frozenAtTrigger = HoldAttackState.FrozenAtTrigger; if (pendingAttack != null) { HoldAttackState.MarkReleased(); if (ModConfig.DebugLog.Value) { float num = Time.time - HoldAttackState.HoldStartTime; string arg = (frozenAtTrigger ? "trigger-fallback" : "windup"); Plugin.Log.LogInfo((object)$"[Hold] released after {num:F2}s (forced={forced}, path={arg})"); } if (frozenAtTrigger) { pendingAttack.OnAttackTrigger(); } } } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class HoldAttackStartGuardPatch { [HarmonyPrefix] private static bool Prefix(Humanoid __instance, ref bool __result) { if (!ModConfig.HoldEnabled.Value) { return true; } if (!HoldAttackState.IsActive) { return true; } if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } __result = false; if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)"[Hold/StartGuard] blocked StartAttack while holding deferred swing"); } return false; } } internal static class HoldAttackState { public static Attack PendingAttack; public static Attack DecidedAttack; public static CharacterAnimEvent FrozenAnimEvent; public static float HoldStartTime; public static bool FrozenAtTrigger; private const float FreezeDurationSeconds = 600f; public static bool TriggerReentryGuard; public static bool IsActive => PendingAttack != null; public static void Freeze(Attack attack, CharacterAnimEvent animEvent, bool atTrigger) { PendingAttack = attack; DecidedAttack = attack; HoldStartTime = Time.time; FrozenAtTrigger = atTrigger; FrozenAnimEvent = animEvent; if ((Object)(object)animEvent != (Object)null) { animEvent.FreezeFrame(600f); } } public static void MarkLetThrough(Attack attack) { DecidedAttack = attack; } public static void MarkReleased() { Thaw(); DecidedAttack = PendingAttack; PendingAttack = null; FrozenAnimEvent = null; } public static void Bailout() { Thaw(); PendingAttack = null; DecidedAttack = null; FrozenAtTrigger = false; HoldStartTime = 0f; FrozenAnimEvent = null; } private static void Thaw() { if (!((Object)(object)FrozenAnimEvent == (Object)null)) { if ((Object)(object)FrozenAnimEvent.m_animator != (Object)null) { float speed = ((FrozenAnimEvent.m_pauseSpeed > 0.01f) ? FrozenAnimEvent.m_pauseSpeed : 1f); FrozenAnimEvent.m_animator.speed = speed; } FrozenAnimEvent.m_pauseTimer = 0f; } } } internal static class HoldAttackTriggerDeferLogic { public static bool TryDefer(Humanoid attacker, Attack attack, string source) { if (!ModConfig.HoldEnabled.Value) { return true; } if (attack == null) { return true; } if (!HoldAttackHelpers.IsLocalPlayer(attacker)) { return true; } if (HoldAttackState.TriggerReentryGuard) { return true; } if (HoldAttackState.PendingAttack == attack) { return true; } if (HoldAttackState.DecidedAttack == attack) { if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)("[Hold/" + source + "] DAMAGE — trigger fired for already-decided attack")); } return true; } if (HoldAttackHelpers.IsProjectile(attack)) { return true; } bool currentAttackIsSecondary = attacker.m_currentAttackIsSecondary; if (currentAttackIsSecondary && !ModConfig.HoldSecondary.Value) { return true; } if (!currentAttackIsSecondary && !ModConfig.HoldPrimary.Value) { return true; } bool flag = ValheimButtons.AttackHeld(currentAttackIsSecondary); if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)($"[Hold/{source}] trigger fired. secondary={currentAttackIsSecondary} " + $"zinputHeld={flag} m_attackHold={((Character)attacker).m_attackHold} " + $"m_secAttackHold={((Character)attacker).m_secondaryAttackHold}")); } if (!flag) { return true; } CharacterAnimEvent animEvent = HoldAttackHelpers.GetAnimEvent(attacker); HoldAttackState.Freeze(attack, animEvent, atTrigger: true); HoldAttackState.TriggerReentryGuard = true; if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)("[Hold/" + source + "] DEFERRED at trigger frame (fallback path)")); } return false; } } [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] internal static class AttackOnAttackTriggerPatch { [HarmonyPrefix] private static bool Prefix(Attack __instance) { return HoldAttackTriggerDeferLogic.TryDefer(__instance.m_character, __instance, "Attack"); } } [HarmonyPatch(typeof(Humanoid), "OnAttackTrigger")] internal static class HumanoidOnAttackTriggerPatch { [HarmonyPrefix] private static bool Prefix(Humanoid __instance) { return HoldAttackTriggerDeferLogic.TryDefer(__instance, __instance.m_currentAttack, "Humanoid"); } } [HarmonyPatch(typeof(Humanoid), "UpdateAttack")] internal static class HoldAttackWindupFreezePatch { [HarmonyPostfix] private static void Postfix(Humanoid __instance) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.HoldEnabled.Value || HoldAttackState.IsActive || !HoldAttackHelpers.IsLocalPlayer(__instance)) { return; } Attack currentAttack = __instance.m_currentAttack; if (currentAttack == null || currentAttack == HoldAttackState.DecidedAttack || HoldAttackHelpers.IsProjectile(currentAttack)) { return; } bool currentAttackIsSecondary = __instance.m_currentAttackIsSecondary; if ((currentAttackIsSecondary && !ModConfig.HoldSecondary.Value) || (!currentAttackIsSecondary && !ModConfig.HoldPrimary.Value)) { return; } Animator animator = ((Character)__instance).m_animator; if ((Object)(object)animator == (Object)null || animator.IsInTransition(0)) { return; } AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); if (((AnimatorStateInfo)(ref currentAnimatorStateInfo)).tagHash != Humanoid.s_animatorTagAttack) { return; } float normalizedTime = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime; if (normalizedTime >= 1f) { return; } float value = ModConfig.HoldFreezeAt.Value; if (normalizedTime < value) { return; } if (!ValheimButtons.AttackHeld(currentAttackIsSecondary)) { HoldAttackState.MarkLetThrough(currentAttack); if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)$"[Hold/Windup] tap detected at t={normalizedTime:F2} — passing through"); } return; } CharacterAnimEvent animEvent = HoldAttackHelpers.GetAnimEvent(__instance); HoldAttackState.Freeze(currentAttack, animEvent, atTrigger: false); if (ModConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)$"[Hold/Windup] frozen at t={normalizedTime:F2} (threshold={value:F2})"); } } } } namespace ImmersiveCombat.Features.AttackCancel { internal static class AttackCancelLogic { public static void CancelAttack(Player player, Attack attack, bool refundStamina) { if (HoldAttackState.PendingAttack == attack) { HoldAttackState.Bailout(); } if (refundStamina) { float num = attack.GetAttackStamina() * ModConfig.RefundStaminaPercent.Value; if (num > 0f) { ((Character)player).AddStamina(num); } } attack.m_abortAttack = true; attack.Stop(); ((Humanoid)player).m_currentAttack = null; ((Humanoid)player).m_currentAttackIsSecondary = false; ((Humanoid)player).m_previousAttack = null; player.m_queuedAttackTimer = 0f; player.m_queuedSecondAttackTimer = 0f; Animator animator = ((Character)player).m_animator; if ((Object)(object)animator != (Object)null) { animator.CrossFadeInFixedTime("Movement", 0.05f, 0); } } } [HarmonyPatch(typeof(Player), "Update")] internal static class AttackCancelPatch { [HarmonyPrefix] private static void Prefix(Player __instance) { if (!ModConfig.CancelEnabled.Value || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !((Character)__instance).InAttack()) { return; } Attack currentAttack = ((Humanoid)__instance).m_currentAttack; if (currentAttack == null) { return; } bool flag = ModConfig.CancelOnBlock.Value && ValheimButtons.BlockPressed(); bool flag2 = ModConfig.CancelOnDodge.Value && ValheimButtons.DodgePressed(); if (flag2 && HoldAttackState.IsActive && ModConfig.HoldLockMovement.Value) { flag2 = false; } if ((flag || flag2) && (!ModConfig.CancelOnlyDuringWindup.Value || !currentAttack.m_attackDone)) { AttackCancelLogic.CancelAttack(__instance, currentAttack, ModConfig.RefundStamina.Value); if (ModConfig.DebugLog.Value) { string arg = (flag ? "Block" : "Dodge"); Plugin.Log.LogInfo((object)$"[Cancel] Aborted swing via {arg}. attackDone={currentAttack.m_attackDone}"); } } } } } namespace ImmersiveCombat.Config { internal static class ModConfig { private const string SecDebug = "0 - Debug"; private const string SecCancel = "1 - Attack Cancel"; private const string SecHold = "2 - Hold To Release Attack"; private const string SecQuickDodge = "3 - Quick Dodge (BETA)"; public static ConfigEntry<bool> DebugLog { get; private set; } public static ConfigEntry<bool> CancelEnabled { get; private set; } public static ConfigEntry<bool> CancelOnBlock { get; private set; } public static ConfigEntry<bool> CancelOnDodge { get; private set; } public static ConfigEntry<bool> CancelOnlyDuringWindup { get; private set; } public static ConfigEntry<bool> RefundStamina { get; private set; } public static ConfigEntry<float> RefundStaminaPercent { get; private set; } public static ConfigEntry<bool> QuickDodgeEnabled { get; private set; } public static ConfigEntry<KeyCode> QuickDodgeKey { get; private set; } public static ConfigEntry<float> QuickDodgeDistanceScale { get; private set; } public static ConfigEntry<float> QuickDodgeStaminaFraction { get; private set; } public static ConfigEntry<float> QuickDodgeIframeSeconds { get; private set; } public static ConfigEntry<bool> HoldEnabled { get; private set; } public static ConfigEntry<bool> HoldPrimary { get; private set; } public static ConfigEntry<bool> HoldSecondary { get; private set; } public static ConfigEntry<float> HoldFreezeAt { get; private set; } public static ConfigEntry<float> HoldMaxSeconds { get; private set; } public static ConfigEntry<bool> HoldLockMovement { get; private set; } public static ConfigEntry<bool> HoldDrainStamina { get; private set; } public static ConfigEntry<float> HoldDrainStaminaPerSecond { get; private set; } public static void Bind(ConfigFile cfg) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Expected O, but got Unknown //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Expected O, but got Unknown //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Expected O, but got Unknown DebugLog = cfg.Bind<bool>("0 - Debug", "DebugLog", false, "Verbose logging to BepInEx\\LogOutput.log."); CancelEnabled = cfg.Bind<bool>("1 - Attack Cancel", "Enabled", true, "Master toggle for the Attack Cancel feature."); CancelOnBlock = cfg.Bind<bool>("1 - Attack Cancel", "CancelOnBlock", true, "Pressing Block during a swing cancels the swing."); CancelOnDodge = cfg.Bind<bool>("1 - Attack Cancel", "CancelOnDodge", true, "Pressing Dodge (Jump key + WASD) during a swing cancels the swing."); CancelOnlyDuringWindup = cfg.Bind<bool>("1 - Attack Cancel", "CancelOnlyDuringWindup", false, "Only allow cancelling before the damage trigger has fired."); RefundStamina = cfg.Bind<bool>("1 - Attack Cancel", "RefundStamina", true, "Refund part of the stamina consumed by the cancelled swing."); RefundStaminaPercent = cfg.Bind<float>("1 - Attack Cancel", "RefundStaminaPercent", 0.5f, new ConfigDescription("Fraction of the attack's stamina cost to refund (0..1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); HoldEnabled = cfg.Bind<bool>("2 - Hold To Release Attack", "Enabled", true, "Master toggle. Holding the attack button freezes the swing in wind-up; releasing it plays the strike."); HoldPrimary = cfg.Bind<bool>("2 - Hold To Release Attack", "HoldPrimary", true, "Apply hold-to-release to primary (LMB) attacks."); HoldSecondary = cfg.Bind<bool>("2 - Hold To Release Attack", "HoldSecondary", false, "Apply hold-to-release to secondary (RMB) attacks. Off by default — vanilla secondary moves are designed to fire instantly."); HoldFreezeAt = cfg.Bind<float>("2 - Hold To Release Attack", "FreezeAtNormalizedTime", 0.35f, new ConfigDescription("Where in the swing animation (0..1) to freeze. Lower = earlier wind-up pose.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 0.95f), Array.Empty<object>())); HoldMaxSeconds = cfg.Bind<float>("2 - Hold To Release Attack", "MaxHoldSeconds", 5f, new ConfigDescription("Auto-release after this many seconds (safety cap).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), Array.Empty<object>())); HoldLockMovement = cfg.Bind<bool>("2 - Hold To Release Attack", "LockMovementWhileHeld", true, "Zero out movement / jump / dodge inputs while the wind-up is frozen, so accidental key presses don't waste stamina. Block still works (cancels)."); HoldDrainStamina = cfg.Bind<bool>("2 - Hold To Release Attack", "DrainStaminaWhileHolding", false, "Drain stamina each second while holding. Off by default."); HoldDrainStaminaPerSecond = cfg.Bind<float>("2 - Hold To Release Attack", "DrainStaminaPerSecond", 4f, new ConfigDescription("Stamina per second drained while holding (only used if DrainStaminaWhileHolding).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); QuickDodgeEnabled = cfg.Bind<bool>("3 - Quick Dodge (BETA)", "Enabled", false, "BETA. Short Witcher 3-style sidestep on a dedicated key, half stamina, shortened iframes. Off by default while this is still in beta."); QuickDodgeKey = cfg.Bind<KeyCode>("3 - Quick Dodge (BETA)", "KeyBind", (KeyCode)308, "BETA. Key that triggers Quick Dodge. Combine with WASD to pick direction; no WASD = backward."); QuickDodgeDistanceScale = cfg.Bind<float>("3 - Quick Dodge (BETA)", "DistanceScale", 0.4f, new ConfigDescription("BETA. Fraction of a normal dodge's distance AND duration.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); QuickDodgeStaminaFraction = cfg.Bind<float>("3 - Quick Dodge (BETA)", "StaminaFraction", 0.5f, new ConfigDescription("BETA. Fraction of vanilla dodge stamina cost.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); QuickDodgeIframeSeconds = cfg.Bind<float>("3 - Quick Dodge (BETA)", "IframeSeconds", 0.05f, new ConfigDescription("BETA. Seconds of invincibility after dodge starts (vanilla ≈ 0.2s).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>())); } } } namespace ImmersiveCombat.Common { internal static class AnimatorConstants { public const string MovementState = "Movement"; public const int BodyLayer = 0; public const float ShortCrossFade = 0.05f; } internal static class ValheimButtons { public const string Jump = "Jump"; public const string JumpJoy = "JoyDodge"; public const string Block = "Block"; public const string BlockJoy = "JoyBlock"; public const string Attack = "Attack"; public const string AttackJoy = "JoyAttack"; public const string SecAttack = "SecondaryAttack"; public const string SecAttackJoy = "JoySecondaryAttack"; public static bool Pressed(string kb, string joy) { if (!ZInput.GetButtonDown(kb)) { return ZInput.GetButtonDown(joy); } return true; } public static bool Held(string kb, string joy) { if (!ZInput.GetButton(kb)) { return ZInput.GetButton(joy); } return true; } public static bool BlockPressed() { return Pressed("Block", "JoyBlock"); } public static bool DodgePressed() { return Pressed("Jump", "JoyDodge"); } public static bool AttackHeld(bool secondary) { if (!secondary) { return Held("Attack", "JoyAttack"); } return Held("SecondaryAttack", "JoySecondaryAttack"); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }