using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; 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: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Valheim Overhaul")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Reload crossbows while sprinting (vanilla speed); slightly faster reload while standing or walking.")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("CrossbowSprintReload")] [assembly: AssemblyTitle("CrossbowSprintReload")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.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 CrossbowSprintReload { internal static class ReloadUtil { internal const string LoadedCustomDataKey = "VO_CrossbowLoaded"; internal static bool ForceWipeReload; internal static int HolsterDepth; private static MethodInfo _setWeaponLoaded; internal static bool IsReloadWeapon(ItemData weapon) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if (weapon?.m_shared?.m_attack == null || !weapon.m_shared.m_attack.m_requiresReload) { return false; } if ((int)weapon.m_shared.m_skillType == 14) { return true; } return Plugin.AffectDundr.Value; } internal static bool IsKeepLoadedCandidate(ItemData weapon) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 if (weapon?.m_shared?.m_attack == null) { return false; } if ((int)weapon.m_shared.m_skillType == 14) { return true; } if (Plugin.AffectDundr.Value) { return weapon.m_shared.m_attack.m_requiresReload; } return false; } internal static MinorActionData FindReloadAction(Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 if (player?.m_actionQueue == null) { return null; } for (int i = 0; i < player.m_actionQueue.Count; i++) { MinorActionData val = player.m_actionQueue[i]; if (val != null && (int)val.m_type == 2 && IsReloadWeapon(val.m_item)) { return val; } } return null; } internal static bool IsSprintIntent(Player player) { if ((Object)(object)player == (Object)null) { return false; } if (((Character)player).IsRunning()) { return true; } try { if (ZInput.GetButton("Run") || ZInput.GetButton("JoyRun")) { return true; } } catch { try { if (ZInput.GetButton("Run")) { return true; } } catch { } } return false; } internal static void MarkLoaded(ItemData item, bool loaded) { if (item?.m_customData != null) { if (loaded) { item.m_customData["VO_CrossbowLoaded"] = "1"; } else { item.m_customData.Remove("VO_CrossbowLoaded"); } } } internal static bool WasMarkedLoaded(ItemData item) { if (item?.m_customData != null && item.m_customData.TryGetValue("VO_CrossbowLoaded", out var value)) { return value == "1"; } return false; } internal static void SetWeaponLoaded(Player player, ItemData weapon) { if (!((Object)(object)player == (Object)null)) { if (_setWeaponLoaded == null) { _setWeaponLoaded = AccessTools.Method(typeof(Player), "SetWeaponLoaded", new Type[1] { typeof(ItemData) }, (Type[])null); } _setWeaponLoaded?.Invoke(player, new object[1] { weapon }); } } } [HarmonyPatch(typeof(Player), "ClearActionQueue")] internal static class PlayerClearActionQueuePatch { private static bool Prefix(Player __instance) { if (!Plugin.Enabled.Value || !Plugin.AllowSprintReload.Value) { return true; } if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (ReloadUtil.ForceWipeReload) { ReloadUtil.ForceWipeReload = false; return true; } MinorActionData val = ReloadUtil.FindReloadAction(__instance); if (val == null) { return true; } __instance.m_actionQueue.Clear(); __instance.m_actionQueue.Add(val); return false; } } [HarmonyPatch(typeof(Player), "OnJump")] internal static class PlayerOnJumpPatch { private static void Prefix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.Enabled.Value && !Plugin.AllowJumpReload.Value) { ReloadUtil.ForceWipeReload = true; } } } [HarmonyPatch(typeof(Player), "UpdateActionQueue")] internal static class PlayerUpdateActionQueuePatch { private static void Prefix(Player __instance, float dt) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 if (!Plugin.Enabled.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || dt <= 0f || __instance.m_actionQueue == null || __instance.m_actionQueue.Count == 0) { return; } MinorActionData val = __instance.m_actionQueue[0]; if (val != null && (int)val.m_type == 2 && ReloadUtil.IsReloadWeapon(val.m_item) && !ReloadUtil.IsSprintIntent(__instance)) { float value = Plugin.NonSprintReloadSpeed.Value; if (!(value <= 1.001f)) { val.m_time += dt * (value - 1f); } } } } [HarmonyPatch(typeof(Humanoid), "HideHandItems")] internal static class HumanoidHideHandItemsPatch { private static void Prefix() { ReloadUtil.HolsterDepth++; } private static void Finalizer() { if (ReloadUtil.HolsterDepth > 0) { ReloadUtil.HolsterDepth--; } } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] internal static class HumanoidUnequipItemPatch { private static void Prefix(Humanoid __instance, ItemData item) { if (Plugin.Enabled.Value && Plugin.KeepLoadedOnHolster.Value && !Plugin.HasSaveCrossbowState && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && item != null && ReloadUtil.IsKeepLoadedCandidate(item)) { if (ReloadUtil.HolsterDepth > 0) { Player localPlayer = Player.m_localPlayer; bool loaded = (Object)(object)localPlayer != (Object)null && ((Humanoid)localPlayer).IsWeaponLoaded() && (((Humanoid)localPlayer).GetCurrentWeapon() == item || ((Humanoid)localPlayer).m_rightItem == item || ((Humanoid)localPlayer).m_leftItem == item); ReloadUtil.MarkLoaded(item, loaded); } else { ReloadUtil.MarkLoaded(item, loaded: false); } } } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] internal static class HumanoidEquipItemPatch { private static void Postfix(Humanoid __instance, ItemData item, bool __result) { if (__result && Plugin.Enabled.Value && Plugin.KeepLoadedOnHolster.Value && !Plugin.HasSaveCrossbowState && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && item != null && ReloadUtil.WasMarkedLoaded(item)) { ReloadUtil.SetWeaponLoaded(Player.m_localPlayer, item); } } } [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] internal static class AttackOnAttackTriggerPatch { private static void Prefix(Attack __instance) { if (Plugin.Enabled.Value && Plugin.KeepLoadedOnHolster.Value && !Plugin.HasSaveCrossbowState && !((Object)(object)__instance?.m_character == (Object)null) && ((Character)__instance.m_character).IsPlayer() && !((Object)(object)__instance.m_character != (Object)(object)Player.m_localPlayer)) { ItemData currentWeapon = __instance.m_character.GetCurrentWeapon(); if (currentWeapon != null) { ReloadUtil.MarkLoaded(currentWeapon, loaded: false); } } } } [BepInPlugin("valheimoverhaul.crossbow.sprintreload", "Crossbow Sprint Reload", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "valheimoverhaul.crossbow.sprintreload"; public const string PluginName = "Crossbow Sprint Reload"; public const string PluginVersion = "1.1.1"; internal static Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry AllowSprintReload; internal static ConfigEntry AllowJumpReload; internal static ConfigEntry NonSprintReloadSpeed; internal static ConfigEntry AffectDundr; internal static ConfigEntry KeepLoadedOnHolster; internal static bool HasSaveCrossbowState; private void Awake() { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable crossbow sprint reload tweaks."); AllowSprintReload = ((BaseUnityPlugin)this).Config.Bind("Reload", "Allow Sprint Reload", true, "Keep reloading while sprinting (vanilla cancels reload on sprint)."); AllowJumpReload = ((BaseUnityPlugin)this).Config.Bind("Reload", "Allow Jump Reload", false, "Keep reloading while jumping (vanilla cancels reload on jump)."); NonSprintReloadSpeed = ((BaseUnityPlugin)this).Config.Bind("Reload", "Non-Sprint Reload Speed", 1.5f, new ConfigDescription("Reload speed while standing still or walking/trotting (not sprinting). 1.0 = vanilla, 1.5 = 50% faster, 2.0 = twice as fast.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 3f), Array.Empty())); AffectDundr = ((BaseUnityPlugin)this).Config.Bind("Reload", "Affect Dundr", true, "Also apply these rules to Dundr (staff that uses the reload system)."); KeepLoadedOnHolster = ((BaseUnityPlugin)this).Config.Bind("Reload", "Keep Loaded When Holstered", true, "Keep a loaded bolt only when holstering to the back (R / HideHandItems, e.g. Weapon Holster Overhaul). Fully unequipping or swapping via hotbar clears the load and requires a reload."); HasSaveCrossbowState = Chainloader.PluginInfos.ContainsKey("Azumatt.SaveCrossbowState"); if (HasSaveCrossbowState) { Log.LogInfo((object)"SaveCrossbowState detected — Keep Loaded When Holstered will defer to that mod."); } new Harmony("valheimoverhaul.crossbow.sprintreload").PatchAll(); Log.LogInfo((object)"Crossbow Sprint Reload 1.1.1 loaded."); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }