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 EasyWeapon v1.1.0
plugins/EasyWeapon/EasyWeapon.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; 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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("EasyWeapon")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("EasyWeapon")] [assembly: AssemblyTitle("EasyWeapon")] [assembly: AssemblyVersion("1.1.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 EasyWeapon { internal static class ChainLightningTuning { internal const string RedPrefabName = "ChainLightningRed"; private const string ChainPrefabPrefix = "ChainLightning"; private static bool weaponApplied; private static bool effectApplied; internal static void ApplyWeaponProcChance(string source) { try { if (weaponApplied) { return; } if (!EasyWeaponPlugin.ChainLightningEnabled.Value) { weaponApplied = true; EasyWeaponPlugin.Log.LogInfo((object)("[" + source + "] 连锁闪电强化已关闭(Enabled=false),武器侧不改。")); return; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { EasyWeaponPlugin.Log.LogWarning((object)("[" + source + "] ObjectDB 还没就绪,武器侧稍后重试。")); return; } if (instance.m_items == null) { EasyWeaponPlugin.Log.LogWarning((object)("[" + source + "] ObjectDB.m_items 为 null,跳过。")); return; } float num = Mathf.Clamp01(EasyWeaponPlugin.ChainProcChance.Value); bool value = EasyWeaponPlugin.ChainLightningVerbose.Value; StringBuilder stringBuilder = new StringBuilder(); int num2 = 0; int num3 = 0; foreach (GameObject item in instance.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null) { continue; } SharedData shared = component.m_itemData.m_shared; if (shared != null) { int num4 = 0; num4 += SetProcChance(shared.m_attack, num, shared.m_name, "主攻击", stringBuilder, value); num4 += SetProcChance(shared.m_secondaryAttack, num, shared.m_name, "副攻击", stringBuilder, value); if (num4 > 0) { num2 += num4; num3++; } } } weaponApplied = true; EasyWeaponPlugin.Log.LogInfo((object)$"[{source}] 武器连锁触发率 → {num:0.###}(主/副攻击统一):命中 {num2} 个攻击槽 / {num3} 件武器(共扫 {instance.m_items.Count} 件物品)。"); if (value && stringBuilder.Length > 0) { EasyWeaponPlugin.Log.LogInfo((object)("---- 武器连锁触发率改动明细 ----" + Environment.NewLine + stringBuilder)); } } catch (Exception ex) { EasyWeaponPlugin.Log.LogError((object)("[武器连锁] 应用失败(游戏本身不受影响):" + ex)); } } private static int SetProcChance(Attack atk, float chance, string itemKey, string slot, StringBuilder detail, bool verbose) { if (atk == null) { return 0; } if (atk.m_spawnOnHitChance <= 0f) { return 0; } if ((Object)(object)atk.m_spawnOnHit == (Object)null) { return 0; } if (!((Object)atk.m_spawnOnHit).name.StartsWith("ChainLightning", StringComparison.Ordinal)) { return 0; } float spawnOnHitChance = atk.m_spawnOnHitChance; atk.m_spawnOnHitChance = chance; if (verbose && detail.Length < 1600) { detail.AppendFormat(" {0}({1})→ {2}:{3:0.###} → {4:0.###}", itemKey, slot, ((Object)atk.m_spawnOnHit).name, spawnOnHitChance, chance); detail.Append(Environment.NewLine); } return 1; } internal static void ApplyChainEffectTuning(string source) { try { if (effectApplied) { return; } if (!EasyWeaponPlugin.ChainLightningEnabled.Value) { effectApplied = true; EasyWeaponPlugin.Log.LogInfo((object)("[" + source + "] 连锁闪电强化已关闭(Enabled=false),特效侧不改。")); return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { EasyWeaponPlugin.Log.LogWarning((object)("[" + source + "] ZNetScene 还没就绪,特效侧稍后重试。")); return; } GameObject val = FindPrefab(instance, "ChainLightningRed"); if ((Object)(object)val == (Object)null) { EasyWeaponPlugin.Log.LogError((object)("[" + source + "] 找不到预制体 ChainLightningRed —— 连锁特效参数不会生效(游戏更新可能改了名字)。")); return; } Aoe component = val.GetComponent<Aoe>(); if ((Object)(object)component == (Object)null) { EasyWeaponPlugin.Log.LogError((object)("[" + source + "] 预制体 ChainLightningRed 上没有 Aoe 组件 —— 参数不会生效(游戏更新可能改了结构)。")); return; } float num = Mathf.Clamp01(EasyWeaponPlugin.RedStartChance.Value); float num2 = Mathf.Clamp01(EasyWeaponPlugin.RedChancePerTarget.Value); float num3 = Mathf.Max(0f, EasyWeaponPlugin.RedFalloff.Value); float num4 = Mathf.Max(0f, EasyWeaponPlugin.RedStartDelay.Value); float num5 = Mathf.Max(0.1f, EasyWeaponPlugin.RedRadius.Value); float num6 = Mathf.Max(0f, EasyWeaponPlugin.RedLightningDamage.Value); float num7 = EasyWeaponPlugin.RedTtl.Value; bool flag = num7 > 0f; if (flag) { num7 = Mathf.Max(0.1f, num7); } int num8 = Mathf.Max(0, EasyWeaponPlugin.RedMinTargets.Value); int num9 = Mathf.Max(0, EasyWeaponPlugin.RedMaxTargets.Value); if (num8 > num9) { int num10 = num8; num8 = num9; num9 = num10; EasyWeaponPlugin.Log.LogWarning((object)$"[{source}] MinTargets({num9}) 大于 MaxTargets({num8}),已在本次运行中自动交换(请修正配置)。"); } float chainStartChance = component.m_chainStartChance; float chainChancePerTarget = component.m_chainChancePerTarget; int chainMinTargets = component.m_chainMinTargets; int chainMaxTargets = component.m_chainMaxTargets; float chainStartChanceFalloff = component.m_chainStartChanceFalloff; float chainStartDelay = component.m_chainStartDelay; float radius = component.m_radius; float ttl = component.m_ttl; float lightning = component.m_damage.m_lightning; component.m_chainStartChance = num; component.m_chainChancePerTarget = num2; component.m_chainMinTargets = num8; component.m_chainMaxTargets = num9; component.m_chainStartChanceFalloff = num3; component.m_chainStartDelay = num4; component.m_radius = num5; if (flag) { component.m_ttl = num7; } component.m_damage.m_lightning = num6; effectApplied = true; EasyWeaponPlugin.Log.LogInfo((object)string.Format("[{0}] 连锁特效 {1} 已改写:" + Environment.NewLine + " 起始判定 {2:0.###} → {3:0.###}" + Environment.NewLine + " 每目标几率 {4:0.###} → {5:0.###}" + Environment.NewLine + " 目标数 {6}~{7} → {8}~{9}" + Environment.NewLine + " 逐跳衰减 {10:0.###} → {11:0.###}", source, "ChainLightningRed", chainStartChance, num, chainChancePerTarget, num2, chainMinTargets, chainMaxTargets, num8, num9, chainStartChanceFalloff, num3)); string text = (flag ? $"存活 {ttl:0.##} → {num7:0.##} s" : $"存活 {ttl:0.##} s(保持游戏默认,未改动)"); EasyWeaponPlugin.Log.LogInfo((object)$" 起始延迟 {chainStartDelay:0.###} → {num4:0.###} s|半径 {radius:0.##} → {num5:0.##} m|每跳闪电伤害 {lightning:0.##} → {num6:0.##}|{text}"); if ((Object)(object)component.m_chainObj == (Object)null) { EasyWeaponPlugin.Log.LogWarning((object)("[" + source + "] ⚠\ufe0f ChainLightningRed 的 m_chainObj 为空 —— 该特效不会继续往下一跳传播(原版如此?)。")); } } catch (Exception ex) { EasyWeaponPlugin.Log.LogError((object)("[连锁特效] 应用失败(游戏本身不受影响):" + ex)); } } private static GameObject FindPrefab(ZNetScene scene, string name) { GameObject prefab = scene.GetPrefab(name); if ((Object)(object)prefab != (Object)null) { return prefab; } if (scene.m_prefabs != null) { foreach (GameObject prefab2 in scene.m_prefabs) { if ((Object)(object)prefab2 != (Object)null && ((Object)prefab2).name == name) { return prefab2; } } } return null; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ChainProcOnObjectDbPatch { [HarmonyPostfix] private static void Postfix() { ChainLightningTuning.ApplyWeaponProcChance("ObjectDB.Awake"); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ChainEffectOnZNetScenePatch { [HarmonyPostfix] private static void Postfix() { ChainLightningTuning.ApplyChainEffectTuning("ZNetScene.Awake"); } } [BepInPlugin("easyweapon.client.only", "EasyWeapon", "1.1.0")] public class EasyWeaponPlugin : BaseUnityPlugin { public const string Guid = "easyweapon.client.only"; public const string PluginName = "EasyWeapon"; public const string PluginVersion = "1.1.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> ReloadSpeedEnabled; internal static ConfigEntry<float> ReloadTimeMultiplier; internal static ConfigEntry<bool> AffectCrossbows; internal static ConfigEntry<bool> AffectGrapplingHook; internal static ConfigEntry<bool> AffectStaves; internal static ConfigEntry<bool> AffectOtherReloadWeapons; internal static ConfigEntry<bool> ReloadSpeedVerbose; internal static ConfigEntry<bool> UninterruptibleEnabled; internal static ConfigEntry<bool> ProtectOnRun; internal static ConfigEntry<bool> ProtectOnJump; internal static ConfigEntry<bool> ProtectOnDodge; internal static ConfigEntry<bool> UninterruptibleVerbose; internal static ConfigEntry<bool> ChainLightningEnabled; internal static ConfigEntry<float> ChainProcChance; internal static ConfigEntry<float> RedStartChance; internal static ConfigEntry<float> RedChancePerTarget; internal static ConfigEntry<int> RedMinTargets; internal static ConfigEntry<int> RedMaxTargets; internal static ConfigEntry<float> RedFalloff; internal static ConfigEntry<float> RedStartDelay; internal static ConfigEntry<float> RedRadius; internal static ConfigEntry<float> RedTtl; internal static ConfigEntry<float> RedLightningDamage; internal static ConfigEntry<bool> ChainLightningVerbose; private Harmony harmony; private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Expected O, but got Unknown //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Expected O, but got Unknown //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Expected O, but got Unknown //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Expected O, but got Unknown //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Expected O, but got Unknown //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Expected O, but got Unknown //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown //IL_0405: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ReloadSpeedEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Reload Speed", "Enabled", true, "装填时间倍率总开关。\n生效对象:弩、钩索(默认),以及可选的闪电法杖 / 其它走装填机制的武器。"); ReloadTimeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Reload Speed", "ReloadTimeMultiplier", 0.5f, new ConfigDescription("装填时间倍率:0.5 = 装填时间减半(更快)。\n⚠\ufe0f 这是「时间」倍率,不是「速度」倍率 —— 想快一倍就填 0.5,填 2 会变慢。\n原版数值:弩 3.5 秒(随弩技能升到 1.75 秒)、钩索 2.0 秒(不吃技能)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>())); AffectCrossbows = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Reload Speed", "Crossbows", true, "弩(CrossbowArbalest / CrossbowRipper 及其变体 / CrossbowGold 及其变体)。"); AffectGrapplingHook = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Reload Speed", "GrapplingHook", true, "钩索。\n注意:游戏里它的本地化键拼写是 $item_graplinghook(少一个 p),不是笔误。"); AffectStaves = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Reload Speed", "Staves", false, "法杖(目前走装填机制的是闪电法杖,充能 1.9 秒)。\n默认关闭 —— 只想要弩和钩索变快的话别开。"); AffectOtherReloadWeapons = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Reload Speed", "OtherReloadWeapons", false, "其它「需要装填」的武器(游戏更新后新加的、本模组还不认识的)。\n默认关闭:宁可不动,也不误伤没见过的武器。"); ReloadSpeedVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Reload Speed", "Verbose", true, "首次遇到某件武器时在日志里记一条(含前后耗时与所用倍率),便于自证生效。\n同一件武器只记一次,不会刷屏。"); UninterruptibleEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Uninterruptible Reload", "Enabled", true, "装填进度不被打断总开关。\n只保护装填动作 —— 队列里的装备动作在奔跑/跳跃/翻滚时**仍然按原版被清掉**。"); ProtectOnRun = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Uninterruptible Reload", "OnRun", true, "奔跑时不打断装填(原版 Player.CheckRun 每帧清空动作队列)。"); ProtectOnJump = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Uninterruptible Reload", "OnJump", true, "跳跃时不打断装填(原版 Player.OnJump 第一条指令就清空动作队列)。"); ProtectOnDodge = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Uninterruptible Reload", "OnDodge", true, "闪避翻滚时不打断装填(原版 Player.UpdateDodge 会清空动作队列)。"); UninterruptibleVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Uninterruptible Reload", "Verbose", true, "每次新的装填动作被保护时,在日志里记一条(同一个装填动作只记一次)。"); ChainLightningEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Chain Lightning", "Enabled", true, "连锁闪电强化总开关(改的是 prefab 静态数据,纯客户端生效)。\n⚠\ufe0f 特效参数只改「雷血 / BloodLightning」那一族用的 ChainLightningRed;\n 另一族 ChainLightning(闪电附魔那批武器用的)本模组**不动**。\n 但武器侧的触发几率是对**所有**连锁武器统一的。"); ChainProcChance = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "WeaponProcChance", 0.66f, new ConfigDescription("所有「命中时生成连锁预制体」的攻击槽,主/副攻击统一用这个几率。\n原版:主攻击 0.25、副攻击 0.20。\n识别判据与游戏自己的 Attack.SpawnOnHit 完全一致(m_spawnOnHitChance > 0、\nm_spawnOnHit 非空,且目标预制体名以 ChainLightning 开头)\n⇒ 不写死武器名单,游戏日后新增连锁武器会自动跟上。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); RedStartChance = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedStartChance", 1f, new ConfigDescription("ChainLightningRed 的起始判定 m_chainStartChance(原版 0.9)。\n⚠\ufe0f 判定发生在特效存活期间**每帧一次**(延迟 RedStartDelay 之后开始),\n 所以 1 = 只要特效还在就必定去找目标 —— 它不是「每次挥击的触发率」,\n 真正的触发率在武器侧(WeaponProcChance)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); RedChancePerTarget = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedChancePerTarget", 0.66f, new ConfigDescription("每个候选目标被链上的几率 m_chainChancePerTarget(原版 0.3)—— 各目标独立判定。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); RedMinTargets = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Chain Lightning", "RedMinTargets", 4, new ConfigDescription("一次连锁最少链几个目标(原版 2)。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 32), Array.Empty<object>())); RedMaxTargets = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Chain Lightning", "RedMaxTargets", 6, new ConfigDescription("一次连锁最多链几个目标(原版 4)。实际个数在 Min~Max 之间随机。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 32), Array.Empty<object>())); RedFalloff = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedFalloff", 0.8f, new ConfigDescription("逐跳衰减 m_chainStartChanceFalloff(原版 0.6):\n传给下一跳对象的几率 = 本跳几率 × 该值。填 1 = 不衰减。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); RedStartDelay = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedStartDelay", 0.18f, new ConfigDescription("特效生成后多久开始判定(秒),原版 0.18。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); RedRadius = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedRadius", 12f, new ConfigDescription("连锁判定半径(米),原版 8。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 64f), Array.Empty<object>())); RedTtl = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedTtl", -1f, new ConfigDescription("连锁特效存活时长(秒)。\n默认 -1 = 保持游戏默认值(3 秒),本模组**完全不碰**这个字段 ——\n将来游戏改了默认值也不会被本模组顶掉。\n填正数(0.1 ~ 30)才会覆盖。\n⚠\ufe0f 起始判定是「存活期间每帧一次」⇒ 它同时是连锁的判定窗口,填小了会明显削弱连锁。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 30f), Array.Empty<object>())); RedLightningDamage = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chain Lightning", "RedLightningDamage", 90f, new ConfigDescription("每一跳的闪电伤害,原版 75。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10000f), Array.Empty<object>())); ChainLightningVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Chain Lightning", "Verbose", true, "在日志里打印每条改动的前后数值(便于自证生效,不刷屏)。"); Log.LogInfo((object)"EasyWeapon 1.1.0 开始挂载 Harmony 补丁……"); if (UninterruptibleEnabled.Value) { UninterruptibleReload.Init(); } ApplyPatches(); VerifyPatches(); } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown harmony = new Harmony("easyweapon.client.only"); Type[] array = new Type[7] { typeof(WeaponLoadingTimePatch), typeof(CheckRunMarkerPatch), typeof(OnJumpMarkerPatch), typeof(UpdateDodgeMarkerPatch), typeof(ProtectedReloadClearPatch), typeof(ChainProcOnObjectDbPatch), typeof(ChainEffectOnZNetScenePatch) }; int num = 0; Type[] array2 = array; foreach (Type type in array2) { try { harmony.CreateClassProcessor(type).Patch(); Log.LogInfo((object)(" 已挂载补丁类:" + type.Name)); num++; } catch (Exception ex) { Log.LogError((object)(" 挂载失败 " + type.Name + ":" + ex.Message)); } } Log.LogInfo((object)$"Harmony 补丁挂载完成:{num}/{array.Length} 成功。"); } private void VerifyPatches() { List<MethodBase> list = new List<MethodBase>(); AddTarget(list, AccessTools.Method(typeof(ItemData), "GetWeaponLoadingTime", (Type[])null, (Type[])null), "ItemData.GetWeaponLoadingTime(装填时间倍率)"); AddTarget(list, AccessTools.Method(typeof(Player), "CheckRun", new Type[2] { typeof(Vector3), typeof(float) }, (Type[])null), "Player.CheckRun(Vector3,float)(奔跑来源标记)"); AddTarget(list, AccessTools.Method(typeof(Player), "OnJump", (Type[])null, (Type[])null), "Player.OnJump(跳跃来源标记)"); AddTarget(list, AccessTools.Method(typeof(Player), "UpdateDodge", new Type[1] { typeof(float) }, (Type[])null), "Player.UpdateDodge(float)(翻滚来源标记)"); AddTarget(list, AccessTools.Method(typeof(Player), "ClearActionQueue", (Type[])null, (Type[])null), "Player.ClearActionQueue(装填不被打断)"); AddTarget(list, AccessTools.Method(typeof(ObjectDB), "Awake", (Type[])null, (Type[])null), "ObjectDB.Awake(连锁闪电:武器触发率)"); AddTarget(list, AccessTools.Method(typeof(ZNetScene), "Awake", (Type[])null, (Type[])null), "ZNetScene.Awake(连锁闪电:特效参数)"); Log.LogInfo((object)"---- 补丁挂载自检 ----"); foreach (MethodBase item in list) { Patches patchInfo = Harmony.GetPatchInfo(item); int num = ((patchInfo != null && patchInfo.Prefixes != null) ? patchInfo.Prefixes.Count : 0); int num2 = ((patchInfo != null && patchInfo.Postfixes != null) ? patchInfo.Postfixes.Count : 0); string text = ((num + num2 > 0) ? "OK" : "!! 未挂上 !!"); Log.LogInfo((object)$" {item.DeclaringType.Name}.{item.Name}() prefix={num} postfix={num2} {text}"); if (patchInfo == null || patchInfo.Prefixes == null) { continue; } foreach (Patch prefix in patchInfo.Prefixes) { if (prefix.owner != "easyweapon.client.only") { Log.LogInfo((object)$" 来自其它模组的 prefix:{prefix.owner}(优先级 {prefix.priority})"); } } } MethodBase methodBase = AccessTools.Method(typeof(Humanoid), "ClearActionQueue", (Type[])null, (Type[])null); if (methodBase != null) { Patches patchInfo2 = Harmony.GetPatchInfo(methodBase); int num3 = ((patchInfo2 != null && patchInfo2.Prefixes != null) ? patchInfo2.Prefixes.Count : 0); Log.LogInfo((object)$" [诊断] Humanoid.ClearActionQueue()(空实现)prefix={num3};(实际执行的是 Player 的重写,见上一行)"); } if (!UninterruptibleReload.QueueFieldResolved) { Log.LogError((object)" !! 反射不到 Player.m_actionQueue —— 「装填不被打断」不会生效(游戏更新可能改了这个字段名)。"); } Log.LogInfo((object)$" (目标方法 {list.Count} 个,全部已核对)"); Log.LogInfo((object)"-----------------------"); } private static void AddTarget(List<MethodBase> targets, MethodBase m, string label) { if (m == null) { Log.LogError((object)(" !! 找不到目标方法,对应功能不会生效:" + label)); } else { targets.Add(m); } } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); } } } internal enum ReloadWeaponKind { Unknown, Crossbow, GrapplingHook, Staff, Other } internal static class ReloadClassifier { internal static string Normalize(string raw) { if (string.IsNullOrEmpty(raw)) { return string.Empty; } string text = raw.Trim(); if (text.Length > 0 && text[0] == '$') { int num = text.IndexOf('_'); text = ((num >= 0) ? text.Substring(num + 1) : text.Substring(1)); } StringBuilder stringBuilder = new StringBuilder(text.Length); string text2 = text; foreach (char c in text2) { if (c != '_' && c != ' ' && c != '-' && c != '.') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } internal static ReloadWeaponKind Classify(ItemData item) { if (item == null || item.m_shared == null) { return ReloadWeaponKind.Unknown; } return ClassifyKey(item.m_shared.m_name); } internal static ReloadWeaponKind ClassifyKey(string rawKey) { string text = Normalize(rawKey); if (text.Length == 0) { return ReloadWeaponKind.Unknown; } if (text == "graplinghook" || text == "grapplinghook") { return ReloadWeaponKind.GrapplingHook; } if (text.StartsWith("crossbow", StringComparison.Ordinal)) { return ReloadWeaponKind.Crossbow; } if (text.StartsWith("staff", StringComparison.Ordinal)) { return ReloadWeaponKind.Staff; } return ReloadWeaponKind.Other; } internal static bool IsAffected(ReloadWeaponKind kind) { return kind switch { ReloadWeaponKind.Crossbow => EasyWeaponPlugin.AffectCrossbows.Value, ReloadWeaponKind.GrapplingHook => EasyWeaponPlugin.AffectGrapplingHook.Value, ReloadWeaponKind.Staff => EasyWeaponPlugin.AffectStaves.Value, ReloadWeaponKind.Other => EasyWeaponPlugin.AffectOtherReloadWeapons.Value, _ => false, }; } internal static string Describe(ReloadWeaponKind kind) { return kind switch { ReloadWeaponKind.Crossbow => "弩", ReloadWeaponKind.GrapplingHook => "钩索", ReloadWeaponKind.Staff => "法杖", ReloadWeaponKind.Other => "其它装填武器", _ => "未识别", }; } } [HarmonyPatch(typeof(ItemData), "GetWeaponLoadingTime")] internal static class WeaponLoadingTimePatch { private static readonly HashSet<string> logged = new HashSet<string>(); private static bool announcedError; [HarmonyPostfix] private static void Postfix(ItemData __instance, ref float __result) { try { if (!EasyWeaponPlugin.ReloadSpeedEnabled.Value || __instance == null || __instance.m_shared == null) { return; } Attack attack = __instance.m_shared.m_attack; if (attack == null || !attack.m_requiresReload) { return; } ReloadWeaponKind kind = ReloadClassifier.Classify(__instance); string text = __instance.m_shared.m_name ?? "?"; if (!ReloadClassifier.IsAffected(kind)) { LogOnce("skip:" + text, $"[装填] 跳过 {text}({ReloadClassifier.Describe(kind)}):该类别的开关是关的。"); return; } float value = EasyWeaponPlugin.ReloadTimeMultiplier.Value; float num = __result; float num2 = num * value; if (!(num <= 0f)) { __result = num2; LogOnce("hit:" + text, $"[装填] {text}({ReloadClassifier.Describe(kind)}):{num:F2}s → {num2:F2}s(倍率 {value})"); } } catch (Exception ex) { if (!announcedError) { announcedError = true; EasyWeaponPlugin.Log.LogWarning((object)("[装填] 计算倍率时出错,本次按原版处理:" + ex.Message)); } } } private static void LogOnce(string dedupKey, string message) { if (EasyWeaponPlugin.ReloadSpeedVerbose.Value && logged.Add(dedupKey)) { EasyWeaponPlugin.Log.LogInfo((object)message); } } } internal static class ActionQueueAccess { private static FieldRef<Player, List<MinorActionData>> queueRef; private static bool initTried; internal static bool QueueFieldResolved { get; private set; } internal static void Init() { if (initTried) { return; } initTried = true; try { queueRef = AccessTools.FieldRefAccess<Player, List<MinorActionData>>("m_actionQueue"); QueueFieldResolved = queueRef != null; } catch (Exception ex) { QueueFieldResolved = false; EasyWeaponPlugin.Log.LogError((object)("反射 Player.m_actionQueue 失败:" + ex.Message)); } } internal static List<MinorActionData> Get(Player player) { if (queueRef == null || (Object)(object)player == (Object)null) { return null; } try { return queueRef.Invoke(player); } catch (Exception) { return null; } } } internal static class InterruptSource { private const int DepthCap = 1000; private static int runDepth; private static int jumpDepth; private static int dodgeDepth; internal static void EnterRun() { if (runDepth < 1000) { runDepth++; } } internal static void ExitRun() { if (runDepth > 0) { runDepth--; } } internal static void EnterJump() { if (jumpDepth < 1000) { jumpDepth++; } } internal static void ExitJump() { if (jumpDepth > 0) { jumpDepth--; } } internal static void EnterDodge() { if (dodgeDepth < 1000) { dodgeDepth++; } } internal static void ExitDodge() { if (dodgeDepth > 0) { dodgeDepth--; } } internal static bool IsProtectedSource() { if (runDepth > 0 && EasyWeaponPlugin.ProtectOnRun.Value) { return true; } if (jumpDepth > 0 && EasyWeaponPlugin.ProtectOnJump.Value) { return true; } if (dodgeDepth > 0 && EasyWeaponPlugin.ProtectOnDodge.Value) { return true; } return false; } internal static string DescribeCurrent() { if (runDepth > 0) { return "奔跑"; } if (jumpDepth > 0) { return "跳跃"; } if (dodgeDepth > 0) { return "闪避翻滚"; } return "未知"; } } [HarmonyPatch(typeof(Player), "CheckRun", new Type[] { typeof(Vector3), typeof(float) })] internal static class CheckRunMarkerPatch { [HarmonyPrefix] private static void Prefix() { InterruptSource.EnterRun(); } [HarmonyPostfix] private static void Postfix() { InterruptSource.ExitRun(); } } [HarmonyPatch(typeof(Player), "OnJump")] internal static class OnJumpMarkerPatch { [HarmonyPrefix] private static void Prefix() { InterruptSource.EnterJump(); } [HarmonyPostfix] private static void Postfix() { InterruptSource.ExitJump(); } } [HarmonyPatch(typeof(Player), "UpdateDodge", new Type[] { typeof(float) })] internal static class UpdateDodgeMarkerPatch { [HarmonyPrefix] private static void Prefix() { InterruptSource.EnterDodge(); } [HarmonyPostfix] private static void Postfix() { InterruptSource.ExitDodge(); } } [HarmonyPatch(typeof(Player), "ClearActionQueue")] [HarmonyPriority(200)] internal static class ProtectedReloadClearPatch { private static MinorActionData lastLogged; private static bool announcedError; [HarmonyPrefix] private static bool Prefix(Player __instance) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 try { if (!EasyWeaponPlugin.UninterruptibleEnabled.Value) { return true; } if (!InterruptSource.IsProtectedSource()) { return true; } List<MinorActionData> list = ActionQueueAccess.Get(__instance); if (list == null || list.Count == 0) { return true; } bool flag = false; for (int i = 0; i < list.Count; i++) { MinorActionData val = list[i]; if (val != null && (int)val.m_type == 2) { flag = true; break; } } if (!flag) { return true; } int num = 0; MinorActionData kept = null; for (int num2 = list.Count - 1; num2 >= 0; num2--) { MinorActionData val2 = list[num2]; if (val2 != null && (int)val2.m_type == 2) { kept = val2; } else { list.RemoveAt(num2); num++; } } LogOnce(kept, num, list.Count); return false; } catch (Exception ex) { if (!announcedError) { announcedError = true; EasyWeaponPlugin.Log.LogWarning((object)("装填保护:判定出错,本次交给原版清空:" + ex.Message)); } return true; } } private static void LogOnce(MinorActionData kept, int removed, int remain) { if (!EasyWeaponPlugin.UninterruptibleVerbose.Value || (kept != null && lastLogged == kept)) { return; } lastLogged = kept; string text = "?"; try { if (kept != null && kept.m_item != null && kept.m_item.m_shared != null) { text = kept.m_item.m_shared.m_name; } } catch (Exception) { } float num = kept?.m_time ?? 0f; float num2 = kept?.m_duration ?? 0f; EasyWeaponPlugin.Log.LogInfo((object)$"[装填保护] {InterruptSource.DescribeCurrent()}触发的清空:保留装填动作「{text}」(进度 {num:F2}s / {num2:F2}s),移除其它动作 {removed} 个,队列现有 {remain} 条。"); } } internal static class UninterruptibleReload { internal static bool QueueFieldResolved => ActionQueueAccess.QueueFieldResolved; internal static void Init() { ActionQueueAccess.Init(); } } }