Decompiled source of ParryBosses v1.0.0

plugins/ParryBosses.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ParryBosses")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("ParryBosses")]
[assembly: AssemblyTitle("ParryBosses")]
[assembly: AssemblyVersion("0.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 ParryBosses
{
	internal static class BossSetup
	{
		public static void Apply(Character c)
		{
			if (!((Object)(object)c == (Object)null) && c.IsBoss() && BossRegistry.Peek(c) == null)
			{
				BossRegistry.Get(c);
				ParryBossesPlugin.LogBasic(c, "boss-awake");
				if (ParryBossesPlugin.CfgForceStaggerWhenBlocked.Value && !c.m_staggerWhenBlocked)
				{
					c.m_staggerWhenBlocked = true;
					ParryBossesPlugin.Log.LogInfo((object)("  -> " + c.m_name + ": m_staggerWhenBlocked false -> true"));
				}
				float value = ParryBossesPlugin.CfgBossStaggerDamageFactor.Value;
				if (value >= 0f && Mathf.Abs(c.m_staggerDamageFactor - value) > 0.0001f)
				{
					ParryBossesPlugin.Log.LogInfo((object)$"  -> {c.m_name}: m_staggerDamageFactor {c.m_staggerDamageFactor} -> {value}");
					c.m_staggerDamageFactor = value;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class Patch_Character_Awake
	{
		private static void Postfix(Character __instance)
		{
			BossSetup.Apply(__instance);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "Awake")]
	internal static class Patch_Humanoid_Awake
	{
		private static void Postfix(Humanoid __instance)
		{
			BossSetup.Apply((Character)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "Stagger", new Type[] { typeof(Vector3) })]
	internal static class Patch_Character_Stagger
	{
		private static void Postfix(Character __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			if (!__instance.IsBoss())
			{
				StaggerMeter.Begin(__instance);
				return;
			}
			BossRegistry.Get(__instance).Probe(__instance);
			switch (ParryBossesPlugin.CfgFallback.Value)
			{
			case StaggerFallbackMode.Always:
				ParryBossesPlugin.StartStagger(__instance, "Stagger()");
				break;
			case StaggerFallbackMode.Auto:
				ParryBossesPlugin.ScheduleFallbackCheck(__instance);
				ParryBossesPlugin.Log.LogInfo((object)(__instance.m_name + " 被僵直:先交给原版动画(Auto 模式,0.12s 后验证)"));
				break;
			default:
				ParryBossesPlugin.Log.LogInfo((object)(__instance.m_name + " 被僵直:仅原版动画(Fallback=Never)"));
				break;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "IsStaggering")]
	internal static class Patch_Character_IsStaggering
	{
		private static bool Prefix(Character __instance, ref bool __result)
		{
			BossState bossState = BossRegistry.Peek(__instance);
			if (bossState == null)
			{
				return true;
			}
			if (bossState.TimerActive)
			{
				__result = true;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ZSyncAnimation), "RPC_SetTrigger")]
	internal static class Patch_ZSyncAnimation_RPC_SetTrigger
	{
		private static bool Prefix(ZSyncAnimation __instance, string name)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance == (Object)null || string.IsNullOrEmpty(name) || name != "stagger")
			{
				return true;
			}
			Character component = ((Component)__instance).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || !component.IsBoss())
			{
				return true;
			}
			if (ParryBossesPlugin.HasAnimatorTrigger(component, "stagger"))
			{
				return true;
			}
			string value = ParryBossesPlugin.CfgSubstituteTrigger.Value;
			if (!string.IsNullOrEmpty(value))
			{
				Animator animator = ParryBossesPlugin.GetAnimator(component);
				if ((Object)(object)animator != (Object)null)
				{
					AnimatorControllerParameter[] parameters = animator.parameters;
					foreach (AnimatorControllerParameter val in parameters)
					{
						if ((int)val.type == 9 && val.name == value)
						{
							animator.SetTrigger(value);
							break;
						}
					}
				}
			}
			return false;
		}
	}
	public enum StaggerFallbackMode
	{
		Always,
		Auto,
		Never
	}
	public enum FlashMode
	{
		PropertyBlock,
		Material
	}
	internal class BossState
	{
		public Character Character;

		public bool HasStaggerTrigger;

		public bool Probed;

		public float StaggeredUntil;

		public bool TimerActive
		{
			get
			{
				if (StaggeredUntil > 0f)
				{
					return Time.time < StaggeredUntil;
				}
				return false;
			}
		}

		public void Probe(Character c)
		{
			if (!Probed)
			{
				HasStaggerTrigger = ParryBossesPlugin.HasAnimatorTrigger(c, "stagger");
				Probed = true;
			}
		}
	}
	internal class StaggerProbe
	{
		public Character Character;

		public float At;

		public string Label;
	}
	internal class StaggerVisual
	{
		public Character Character;

		public float NextEffectAt;

		public GameObject LightGo;

		public MaterialFlash Flash;
	}
	internal static class BossRegistry
	{
		private static readonly Dictionary<int, BossState> States = new Dictionary<int, BossState>();

		public static BossState Get(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)c).GetInstanceID();
			if (States.TryGetValue(instanceID, out var value) && (Object)(object)value.Character == (Object)(object)c)
			{
				return value;
			}
			value = new BossState
			{
				Character = c
			};
			States[instanceID] = value;
			return value;
		}

		public static BossState Peek(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			if (!States.TryGetValue(((Object)c).GetInstanceID(), out var value) || !((Object)(object)value.Character == (Object)(object)c))
			{
				return null;
			}
			return value;
		}

		public static void Prune()
		{
			if (States.Count == 0)
			{
				return;
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, BossState> state in States)
			{
				if ((Object)(object)state.Value.Character == (Object)null)
				{
					list.Add(state.Key);
				}
			}
			foreach (int item in list)
			{
				States.Remove(item);
			}
		}
	}
	[BepInPlugin("dsh.valheim.parrybosses", "Parry Bosses", "1.0.0")]
	public class ParryBossesPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "dsh.valheim.parrybosses";

		public const string PluginName = "Parry Bosses";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> CfgForceStaggerWhenBlocked;

		internal static ConfigEntry<StaggerFallbackMode> CfgFallback;

		internal static ConfigEntry<float> CfgStaggerSeconds;

		internal static ConfigEntry<string> CfgSubstituteTrigger;

		internal static ConfigEntry<float> CfgBossStaggerDamageFactor;

		internal static ConfigEntry<bool> CfgHudOnStagger;

		internal static ConfigEntry<bool> CfgParticles;

		internal static ConfigEntry<float> CfgParticleInterval;

		internal static ConfigEntry<bool> CfgCritEffect;

		internal static ConfigEntry<bool> CfgFloatingText;

		internal static ConfigEntry<string> CfgFloatingTextString;

		internal static ConfigEntry<bool> CfgLight;

		internal static ConfigEntry<float> CfgLightRange;

		internal static ConfigEntry<bool> CfgMaterialFlash;

		internal static ConfigEntry<FlashMode> CfgFlashMode;

		internal static ConfigEntry<float> CfgFlashStrength;

		internal static ConfigEntry<bool> CfgDiagnose;

		internal static ConfigEntry<bool> CfgMeasure;

		internal static ConfigEntry<KeyboardShortcut> CfgDumpKey;

		internal static ConfigEntry<KeyboardShortcut> CfgForceKey;

		internal static ConfigEntry<KeyboardShortcut> CfgForceMobKey;

		private static readonly List<StaggerProbe> Probes = new List<StaggerProbe>();

		private static readonly List<StaggerVisual> Visuals = new List<StaggerVisual>();

		private float _pruneTimer;

		private void Awake()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Expected O, but got Unknown
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			CfgForceStaggerWhenBlocked = ((BaseUnityPlugin)this).Config.Bind<bool>("1-核心", "ForceStaggerWhenBlocked", true, "把 Boss 的 m_staggerWhenBlocked 强制设为 true(原版 Eikthyr 实测本来就是 true,属保险项)。");
			CfgFallback = ((BaseUnityPlugin)this).Config.Bind<StaggerFallbackMode>("1-核心", "StaggerFallback", StaggerFallbackMode.Always, "Always = 每次 Boss 被僵直都用计时器把 IsStaggering() 拉成 true(推荐);Auto = 先让原版动画试,0.12 秒后若动画状态仍未进入 stagger 就自动补计时器;Never = 只用原版。");
			CfgStaggerSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("1-核心", "StaggerSeconds", 2f, new ConfigDescription("Boss 被弹反后的僵直时长(秒)。默认 2.0 —— 所有 Boss 统一 2 秒;僵直期间不能移动/攻击,并承受 2 倍伤害。(参考:原版小怪众数 1.5s,训练假人 3.0s,Fader/The Queen 自带僵直动画也是 2.0s)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			CfgSubstituteTrigger = ((BaseUnityPlugin)this).Config.Bind<string>("2-表现", "SubstituteTrigger", "", "Boss 没有 stagger 触发器时改播的动画触发器;留空 = 不播(Boss 通常连 hit 都没有)。");
			CfgHudOnStagger = ((BaseUnityPlugin)this).Config.Bind<bool>("2-表现", "HudOnStagger", true, "僵直触发时屏幕左上角显示一行提示。");
			CfgCritEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("2-表现", "StaggerCritEffect", true, "僵直开始时在 Boss 身上播放暴击/重击特效(m_critHitEffects),即 2 倍伤害的那个特效。");
			CfgParticles = ((BaseUnityPlugin)this).Config.Bind<bool>("2-表现", "StaggerParticles", true, "僵直期间每隔一段时间在 Boss 身上重复播放受击特效(m_hitEffects),形成持续的火花。");
			CfgParticleInterval = ((BaseUnityPlugin)this).Config.Bind<float>("2-表现", "StaggerParticleInterval", 0.3f, new ConfigDescription("重复特效的间隔(秒)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 3f), Array.Empty<object>()));
			CfgFloatingText = ((BaseUnityPlugin)this).Config.Bind<bool>("2-表现", "StaggerFloatingText", true, "僵直开始时在 Boss 头顶弹出漂浮文字(橙色),便于一眼看出触发了僵直。");
			CfgFloatingTextString = ((BaseUnityPlugin)this).Config.Bind<string>("2-表现", "StaggerFloatingTextString", "STAGGER!", "漂浮文字内容。Boss 字体不含中文,建议用英文,避免出现方块。");
			CfgLight = ((BaseUnityPlugin)this).Config.Bind<bool>("2-表现", "StaggerLight", true, "僵直期间给 Boss 挂一盏暖色点光(闪光),让僵直在视觉上非常明显。");
			CfgLightRange = ((BaseUnityPlugin)this).Config.Bind<float>("2-表现", "StaggerLightRange", 8f, new ConfigDescription("点光半径。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 40f), Array.Empty<object>()));
			CfgMaterialFlash = ((BaseUnityPlugin)this).Config.Bind<bool>("2-表现", "StaggerMaterialFlash", true, "僵直期间给 Boss 做材质闪烁(发光 + 颜色脉冲),比点光更醒目。会实例化一份材质并在僵直结束后恢复原值、销毁副本。");
			CfgFlashStrength = ((BaseUnityPlugin)this).Config.Bind<float>("2-表现", "StaggerFlashStrength", 2f, new ConfigDescription("材质发光强度(仅当材质有 _EmissionColor 时有效)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 8f), Array.Empty<object>()));
			CfgFlashMode = ((BaseUnityPlugin)this).Config.Bind<FlashMode>("2-表现", "StaggerFlashMode", FlashMode.PropertyBlock, "PropertyBlock = 用 MaterialPropertyBlock 覆盖 _Color/_EmissionColor(最安全,推荐);Material = 实例化材质后改色(发光更明显;结束时会先把渲染器材质引用还原成原始共享材质,再销毁实例)。");
			CfgBossStaggerDamageFactor = ((BaseUnityPlugin)this).Config.Bind<float>("3-可选", "BossStaggerDamageFactor", -1f, "小于 0 = 不改动(原版 Boss 为 0)。设为 0.3 之类可让 Boss 也吃普通伤害累积僵直。");
			CfgDiagnose = ((BaseUnityPlugin)this).Config.Bind<bool>("9-诊断", "DiagnoseStagger", true, "僵直后 +0.15s / +0.6s 采样 IsStaggering 与动画状态标签写入日志。");
			CfgMeasure = ((BaseUnityPlugin)this).Config.Bind<bool>("9-诊断", "MeasureStaggerDurations", true, "测量原版动画的真实僵直时长:对小怪/自己按 F9 各试 2~3 次,日志会给出每个角色的平均时长、跨角色中位数和建议的 StaggerSeconds 值。");
			CfgDumpKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("9-诊断", "DumpKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "输出附近角色/Boss 的僵直参数(含特效内容)到日志。");
			CfgForceKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("9-诊断", "ForceStaggerKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "强制僵直最近的 Boss。");
			CfgForceMobKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("9-诊断", "ForceStaggerMobKey", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "强制僵直最近的非 Boss 敌人(对照用)。");
			new Harmony("dsh.valheim.parrybosses").PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)"===== Parry Bosses v1.0.0 已加载 =====");
			Log.LogInfo((object)("  BepInEx 根目录 : " + Paths.BepInExRootPath));
			Log.LogInfo((object)("  插件目录       : " + Paths.PluginPath));
			Log.LogInfo((object)("  配置目录       : " + Paths.ConfigPath));
			Log.LogInfo((object)$"  BepInEx 版本   : {typeof(BaseUnityPlugin).Assembly.GetName().Version}");
			Log.LogInfo((object)$"  Harmony 版本   : {typeof(Harmony).Assembly.GetName().Version}");
			VerifyTargets();
			Log.LogInfo((object)($"  生效配置: ForceStaggerWhenBlocked={CfgForceStaggerWhenBlocked.Value} " + $"Fallback={CfgFallback.Value} StaggerSeconds={CfgStaggerSeconds.Value} | " + $"暴击特效={CfgCritEffect.Value} 持续特效={CfgParticles.Value}/{CfgParticleInterval.Value}s " + $"漂浮文字={CfgFloatingText.Value}(\"{CfgFloatingTextString.Value}\") 点光={CfgLight.Value} " + $"材质闪烁={CfgMaterialFlash.Value}(强度{CfgFlashStrength.Value}) 时长测量={CfgMeasure.Value}"));
			if (CfgFallback.Value != StaggerFallbackMode.Always)
			{
				Log.LogWarning((object)($"注意:StaggerFallback={CfgFallback.Value}。实测 Boss 动画器没有 tag=stagger 的状态," + "只有 Always(或 Auto 的自愈)才会真正产生僵直。可在 " + Paths.ConfigPath + " 里改成 Always。"));
			}
		}

		private static void VerifyTargets()
		{
			(Type, string, Type[])[] array = new(Type, string, Type[])[5]
			{
				(typeof(Character), "Awake", Type.EmptyTypes),
				(typeof(Humanoid), "Awake", Type.EmptyTypes),
				(typeof(Character), "Stagger", new Type[1] { typeof(Vector3) }),
				(typeof(Character), "IsStaggering", Type.EmptyTypes),
				(typeof(ZSyncAnimation), "RPC_SetTrigger", new Type[2]
				{
					typeof(long),
					typeof(string)
				})
			};
			for (int i = 0; i < array.Length; i++)
			{
				(Type, string, Type[]) tuple = array[i];
				MethodBase methodBase = AccessTools.Method(tuple.Item1, tuple.Item2, tuple.Item3, (Type[])null);
				ManualLogSource log = Log;
				string[] obj = new string[8]
				{
					"  目标 ",
					tuple.Item1.Name,
					".",
					tuple.Item2,
					"(",
					null,
					null,
					null
				};
				object[] item = tuple.Item3;
				obj[5] = string.Join(",", item);
				obj[6] = "): ";
				obj[7] = ((methodBase != null) ? "OK" : "★未找到★");
				log.LogInfo((object)string.Concat(obj));
			}
		}

		private void Update()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			_pruneTimer += Time.deltaTime;
			if (_pruneTimer > 5f)
			{
				_pruneTimer = 0f;
				BossRegistry.Prune();
			}
			RunProbes();
			RunVisuals();
			StaggerMeter.Update();
			KeyboardShortcut value = CfgDumpKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				DumpNearby();
			}
			value = CfgForceKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				ForceStagger(onlyBoss: true);
			}
			value = CfgForceMobKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				ForceStagger(onlyBoss: false);
			}
		}

		internal static void StartStagger(Character c, string reason)
		{
			if (!((Object)(object)c == (Object)null))
			{
				BossState bossState = BossRegistry.Get(c);
				bossState.Probe(c);
				float value = CfgStaggerSeconds.Value;
				bossState.StaggeredUntil = Time.time + value;
				Log.LogInfo((object)($"{c.m_name} 进入僵直 {value:0.0}s(来源={reason},Fallback={CfgFallback.Value}," + $"动画有 stagger 触发器={bossState.HasStaggerTrigger})"));
				if (CfgCritEffect.Value)
				{
					PlayEffect(c, c.m_critHitEffects, "暴击");
				}
				StaggerVisual staggerVisual = Visuals.Find((StaggerVisual x) => (Object)(object)x.Character == (Object)(object)c);
				if (staggerVisual == null)
				{
					staggerVisual = new StaggerVisual
					{
						Character = c
					};
					Visuals.Add(staggerVisual);
				}
				staggerVisual.NextEffectAt = Time.time + CfgParticleInterval.Value;
				if (CfgLight.Value)
				{
					EnsureLight(c);
				}
				if (CfgMaterialFlash.Value)
				{
					MaterialFlashHelper.Begin(c, staggerVisual);
				}
				if (CfgFloatingText.Value && !string.IsNullOrEmpty(CfgFloatingTextString.Value))
				{
					ShowFloatingText(c, CfgFloatingTextString.Value);
				}
				if (CfgHudOnStagger.Value)
				{
					Hud($"{c.m_name} 进入僵直 {value:0.0}s — 此时受 2 倍伤害", (MessageType)1);
				}
				ScheduleProbe(c, 0.15f, "+0.15s");
				ScheduleProbe(c, 0.6f, "+0.60s");
			}
		}

		private static void RunVisuals()
		{
			if (Visuals.Count == 0)
			{
				return;
			}
			float time = Time.time;
			for (int num = Visuals.Count - 1; num >= 0; num--)
			{
				StaggerVisual staggerVisual = Visuals[num];
				Character character = staggerVisual.Character;
				BossState bossState = (((Object)(object)character == (Object)null) ? null : BossRegistry.Peek(character));
				if ((Object)(object)character != (Object)null && bossState != null && bossState.TimerActive)
				{
					if (CfgMaterialFlash.Value)
					{
						MaterialFlashHelper.Tick(staggerVisual, 0.5f + 0.5f * Mathf.Sin(Time.time * 14f));
					}
					if (CfgParticles.Value && time >= staggerVisual.NextEffectAt)
					{
						staggerVisual.NextEffectAt = time + CfgParticleInterval.Value;
						PlayEffect(character, character.m_hitEffects, "受击");
					}
				}
				else
				{
					DestroyLight(staggerVisual);
					MaterialFlashHelper.End(staggerVisual);
					Visuals.RemoveAt(num);
				}
			}
		}

		private static void EnsureLight(Character c)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			StaggerVisual staggerVisual = Visuals.Find((StaggerVisual x) => (Object)(object)x.Character == (Object)(object)c);
			if (staggerVisual != null && (Object)(object)staggerVisual.LightGo != (Object)null)
			{
				return;
			}
			try
			{
				GameObject val = new GameObject("ParryBosses_StaggerLight");
				val.transform.SetParent(((Component)c).transform, false);
				Vector3 localPosition = c.GetCenterPoint() - ((Component)c).transform.position;
				val.transform.localPosition = localPosition;
				Light obj = val.AddComponent<Light>();
				obj.type = (LightType)2;
				obj.color = new Color(1f, 0.72f, 0.25f);
				obj.range = CfgLightRange.Value;
				obj.intensity = 2.5f;
				if (staggerVisual == null)
				{
					staggerVisual = new StaggerVisual
					{
						Character = c
					};
					Visuals.Add(staggerVisual);
				}
				staggerVisual.LightGo = val;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("创建僵直点光失败: " + ex.Message));
			}
		}

		private static void DestroyLight(StaggerVisual v)
		{
			if (!((Object)(object)v?.LightGo == (Object)null))
			{
				try
				{
					Object.Destroy((Object)(object)v.LightGo);
				}
				catch
				{
				}
				v.LightGo = null;
			}
		}

		private static void PlayEffect(Character c, EffectList list, string what)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c == (Object)null || list == null)
			{
				return;
			}
			try
			{
				list.Create(c.GetCenterPoint(), Quaternion.identity, ((Component)c).transform, 1f, -1, c.GetZDOID());
			}
			catch (Exception ex)
			{
				Log.LogDebug((object)("播放" + what + "特效失败: " + ex.Message));
			}
		}

		private static void ShowFloatingText(Character c, string text)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)DamageText.instance != (Object)null)
				{
					DamageText.instance.ShowText((TextType)7, c.GetTopPoint(), text, false);
				}
			}
			catch (Exception ex)
			{
				Log.LogDebug((object)("漂浮文字失败: " + ex.Message));
			}
		}

		internal static void ScheduleProbe(Character c, float delay, string label)
		{
			if (CfgDiagnose.Value && !((Object)(object)c == (Object)null))
			{
				Probes.Add(new StaggerProbe
				{
					Character = c,
					At = Time.time + delay,
					Label = label
				});
			}
		}

		internal static void ScheduleFallbackCheck(Character c, float delay = 0.12f)
		{
			Probes.Add(new StaggerProbe
			{
				Character = c,
				At = Time.time + delay,
				Label = "AUTO-CHECK"
			});
		}

		private static void RunProbes()
		{
			if (Probes.Count == 0)
			{
				return;
			}
			float time = Time.time;
			for (int num = Probes.Count - 1; num >= 0; num--)
			{
				StaggerProbe staggerProbe = Probes[num];
				if ((Object)(object)staggerProbe.Character == (Object)null)
				{
					Probes.RemoveAt(num);
				}
				else if (!(time < staggerProbe.At))
				{
					Probes.RemoveAt(num);
					if (staggerProbe.Label == "AUTO-CHECK")
					{
						if (!RawAnimatorStaggering(staggerProbe.Character))
						{
							Log.LogInfo((object)(staggerProbe.Character.m_name + ":Auto 模式自愈 —— 原版动画未进入 stagger 状态,改用计时器"));
							StartStagger(staggerProbe.Character, "Auto自愈");
						}
						else
						{
							Log.LogInfo((object)(staggerProbe.Character.m_name + ":原版动画已进入 stagger 状态,无需计时器"));
						}
					}
					else
					{
						LogStaggerState(staggerProbe.Character, staggerProbe.Label);
					}
				}
			}
		}

		internal static bool RawAnimatorStaggering(Character c)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			int hash = ZSyncAnimation.GetHash("stagger");
			try
			{
				Animator animator = GetAnimator(c);
				if ((Object)(object)animator == (Object)null)
				{
					return false;
				}
				AnimatorStateInfo val = animator.GetCurrentAnimatorStateInfo(0);
				int result;
				if (((AnimatorStateInfo)(ref val)).tagHash != hash)
				{
					val = animator.GetNextAnimatorStateInfo(0);
					result = ((((AnimatorStateInfo)(ref val)).tagHash == hash) ? 1 : 0);
				}
				else
				{
					result = 1;
				}
				return (byte)result != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static void LogStaggerState(Character c, string label)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			BossState bossState = BossRegistry.Peek(c);
			bossState?.Probe(c);
			int hash = ZSyncAnimation.GetHash("stagger");
			int num = 0;
			int num2 = 0;
			string text = null;
			try
			{
				Animator animator = GetAnimator(c);
				if ((Object)(object)animator != (Object)null)
				{
					AnimatorStateInfo val = animator.GetCurrentAnimatorStateInfo(0);
					num = ((AnimatorStateInfo)(ref val)).tagHash;
					val = animator.GetNextAnimatorStateInfo(0);
					num2 = ((AnimatorStateInfo)(ref val)).tagHash;
				}
			}
			catch (Exception ex)
			{
				text = ex.Message;
			}
			Log.LogInfo((object)($"[僵直验证 {label}] {c.m_name} | IsStaggering(经补丁)={c.IsStaggering()} | 计时器生效={bossState?.TimerActive ?? false} | " + $"动画标签 current={num}(=stagger?{num == hash}) next={num2}(=stagger?{num2 == hash})" + ((text != null) ? (" | 读动画异常:" + text) : "")));
		}

		internal static void Hud(string msg, MessageType type = (MessageType)2)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Log.LogInfo((object)("[屏幕提示] " + msg));
			try
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage(type, msg, 0, (Sprite)null, false, true);
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("屏幕提示失败: " + ex.Message));
			}
		}

		private static void DumpNearby()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			List<Character> list = new List<Character>(Character.GetAllCharacters());
			Player localPlayer = Player.m_localPlayer;
			int num = 0;
			int num2 = 0;
			Log.LogInfo((object)$"===== ParryBosses 诊断:共 {list.Count} 个 Character =====");
			foreach (Character item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					bool flag = item.IsBoss();
					if (flag)
					{
						num++;
					}
					bool flag2 = (Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)item).transform.position, ((Component)localPlayer).transform.position) < 40f;
					if (flag || flag2)
					{
						LogCharacter(item, flag ? "boss" : "nearby");
						num2++;
					}
				}
			}
			Log.LogInfo((object)$"===== 诊断结束(输出 {num2} 个,其中 Boss {num} 个)=====");
			StaggerMeter.DumpStats();
			Hud($"ParryBosses 诊断已写入日志:Boss {num} 个 / 附近角色 {num2} 个", (MessageType)2);
		}

		private static void ForceStagger(bool onlyBoss)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			List<Character> list = new List<Character>(Character.GetAllCharacters());
			Player localPlayer = Player.m_localPlayer;
			Character val = null;
			float num = 60f;
			foreach (Character item in list)
			{
				if (!((Object)(object)item == (Object)null) && !item.IsPlayer() && !item.IsDead() && (!onlyBoss || item.IsBoss()) && (onlyBoss || !item.IsBoss()))
				{
					float num2 = (((Object)(object)localPlayer == (Object)null) ? 0f : Vector3.Distance(((Component)item).transform.position, ((Component)localPlayer).transform.position));
					if (num2 < num)
					{
						num = num2;
						val = item;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				Hud(onlyBoss ? "ParryBosses:60 米内没找到 Boss(先 spawn 一只,比如 spawn gd_king)" : "ParryBosses:60 米内没找到非 Boss 敌人", (MessageType)2);
				return;
			}
			LogCharacter(val, onlyBoss ? "force-stagger-boss" : "force-stagger-mob");
			val.Stagger(Vector3.zero);
			Hud($"ParryBosses:已对 {val.m_name} 施加僵直({CfgStaggerSeconds.Value}s)", (MessageType)2);
		}

		internal static void LogBasic(Character c, string tag)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)c == (Object)null))
			{
				Log.LogInfo((object)($"[{tag}] {c.m_name} | boss={c.IsBoss()} hp={c.GetHealth():0.#}/{c.GetMaxHealth():0.#} | " + $"m_staggerWhenBlocked={c.m_staggerWhenBlocked} m_staggerDamageFactor={c.m_staggerDamageFactor} | " + $"faction={c.m_faction}"));
			}
		}

		internal static void LogCharacter(Character c, string tag)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)c == (Object)null))
			{
				Log.LogInfo((object)($"[{tag}] {c.m_name} | boss={c.IsBoss()} hp={c.GetHealth():0.#}/{c.GetMaxHealth():0.#} | " + $"m_staggerWhenBlocked={c.m_staggerWhenBlocked} m_staggerDamageFactor={c.m_staggerDamageFactor} | " + $"IsStaggering={c.IsStaggering()} | 原版动画僵直={RawAnimatorStaggering(c)} | " + string.Format("animator(stagger触发器={0}) | faction={1}", HasAnimatorTrigger(c, "stagger"), c.m_faction)));
				if (c.IsBoss())
				{
					DumpEffectLists(c);
				}
			}
		}

		private static void DumpEffectLists(Character c)
		{
			Log.LogInfo((object)("  特效内容 hit=[" + Names(c.m_hitEffects) + "] crit=[" + Names(c.m_critHitEffects) + "]"));
		}

		private static string Names(EffectList list)
		{
			if (list == null || list.m_effectPrefabs == null)
			{
				return "";
			}
			List<string> list2 = new List<string>();
			EffectData[] effectPrefabs = list.m_effectPrefabs;
			foreach (EffectData val in effectPrefabs)
			{
				if (val != null && (Object)(object)val.m_prefab != (Object)null)
				{
					list2.Add(((Object)val.m_prefab).name);
				}
			}
			return string.Join(", ", list2);
		}

		internal static ZSyncAnimation GetZanim(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			try
			{
				ZSyncAnimation value = Traverse.Create((object)c).Field<ZSyncAnimation>("m_zanim").Value;
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
			}
			catch
			{
			}
			return ((Component)c).GetComponent<ZSyncAnimation>();
		}

		internal static Animator GetAnimator(Character c)
		{
			ZSyncAnimation zanim = GetZanim(c);
			if ((Object)(object)zanim == (Object)null)
			{
				return null;
			}
			try
			{
				Animator value = Traverse.Create((object)zanim).Field<Animator>("m_animator").Value;
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
			}
			catch
			{
			}
			return ((Component)zanim).GetComponent<Animator>();
		}

		internal static bool HasAnimatorTrigger(Character c, string trigger)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			try
			{
				Animator animator = GetAnimator(c);
				if ((Object)(object)animator == (Object)null)
				{
					return false;
				}
				AnimatorControllerParameter[] parameters = animator.parameters;
				foreach (AnimatorControllerParameter val in parameters)
				{
					if ((int)val.type == 9 && val.name == trigger)
					{
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogDebug((object)("HasAnimatorTrigger(" + trigger + ") 失败: " + ex.Message));
			}
			return false;
		}
	}
	internal class MaterialFlash
	{
		public Character Character;

		public readonly List<Renderer> Renderers = new List<Renderer>();

		public readonly MaterialPropertyBlock Block = new MaterialPropertyBlock();

		public readonly List<Material[]> OrigShared = new List<Material[]>();

		public readonly List<Material[]> Instances = new List<Material[]>();

		public int TintableCount;

		public int EmissiveCount;

		public bool UseMaterialMode;
	}
	internal static class MaterialFlashHelper
	{
		public static void Begin(Character c, StaggerVisual v)
		{
			if (v.Flash != null)
			{
				return;
			}
			MaterialFlash materialFlash = new MaterialFlash
			{
				Character = c
			};
			materialFlash.UseMaterialMode = ParryBossesPlugin.CfgFlashMode.Value == FlashMode.Material;
			try
			{
				Renderer[] componentsInChildren = ((Component)c).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials;
					try
					{
						sharedMaterials = val.sharedMaterials;
					}
					catch
					{
						continue;
					}
					bool flag = false;
					bool flag2 = false;
					bool flag3 = sharedMaterials.Length != 0;
					Material[] array = sharedMaterials;
					foreach (Material val2 in array)
					{
						if (!((Object)(object)val2 == (Object)null))
						{
							flag3 = false;
							if (val2.HasProperty("_Color"))
							{
								flag = true;
							}
							if (val2.HasProperty("_EmissionColor"))
							{
								flag2 = true;
							}
						}
					}
					if (flag3)
					{
						ParryBossesPlugin.Log.LogWarning((object)("  " + c.m_name + " 的渲染器 " + ((Object)val).name + " 材质已失效(上个版本销毁材质导致),请重载世界或重启游戏恢复"));
					}
					else
					{
						if (!flag && !flag2)
						{
							continue;
						}
						if (flag)
						{
							materialFlash.TintableCount++;
						}
						if (flag2)
						{
							materialFlash.EmissiveCount++;
						}
						materialFlash.Renderers.Add(val);
						if (!materialFlash.UseMaterialMode)
						{
							continue;
						}
						materialFlash.OrigShared.Add(sharedMaterials);
						Material[] materials = val.materials;
						array = materials;
						foreach (Material val3 in array)
						{
							if ((Object)(object)val3 != (Object)null && val3.HasProperty("_EmissionColor"))
							{
								val3.EnableKeyword("_EMISSION");
							}
						}
						materialFlash.Instances.Add(materials);
					}
				}
			}
			catch (Exception ex)
			{
				ParryBossesPlugin.Log.LogWarning((object)("材质闪烁初始化失败: " + ex.Message));
			}
			v.Flash = materialFlash;
			ParryBossesPlugin.Log.LogInfo((object)($"  材质闪烁[{ParryBossesPlugin.CfgFlashMode.Value}]: {materialFlash.Renderers.Count} 个渲染器" + $"(可调色 {materialFlash.TintableCount} / 可发光 {materialFlash.EmissiveCount})"));
			if (materialFlash.Renderers.Count == 0)
			{
				ParryBossesPlugin.Log.LogWarning((object)"  该角色没有可用 _Color/_EmissionColor 的材质,闪烁不会可见");
			}
		}

		public static void Tick(StaggerVisual v, float k)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			MaterialFlash flash = v.Flash;
			if (flash == null)
			{
				return;
			}
			float value = ParryBossesPlugin.CfgFlashStrength.Value;
			Color val = default(Color);
			((Color)(ref val))..ctor(1f, 0.62f, 0.18f);
			float num = Mathf.Clamp01(0.15f + 0.55f * k);
			Color val2 = Color.Lerp(Color.white, val, num);
			Color val3 = val * (value * k * 0.5f);
			if (!flash.UseMaterialMode)
			{
				foreach (Renderer renderer in flash.Renderers)
				{
					if (!((Object)(object)renderer == (Object)null))
					{
						try
						{
							flash.Block.Clear();
							flash.Block.SetColor("_Color", val2);
							if (value > 0f)
							{
								flash.Block.SetColor("_EmissionColor", val3);
							}
							renderer.SetPropertyBlock(flash.Block);
						}
						catch
						{
						}
					}
				}
				return;
			}
			for (int i = 0; i < flash.Instances.Count; i++)
			{
				Material[] array = flash.Instances[i];
				foreach (Material val4 in array)
				{
					if ((Object)(object)val4 == (Object)null)
					{
						continue;
					}
					try
					{
						if (val4.HasProperty("_Color"))
						{
							val4.SetColor("_Color", val2);
						}
						if (value > 0f && val4.HasProperty("_EmissionColor"))
						{
							val4.SetColor("_EmissionColor", val3);
						}
					}
					catch
					{
					}
				}
			}
		}

		public static void End(StaggerVisual v)
		{
			MaterialFlash flash = v.Flash;
			if (flash == null)
			{
				return;
			}
			for (int i = 0; i < flash.Renderers.Count; i++)
			{
				Renderer val = flash.Renderers[i];
				if ((Object)(object)val != (Object)null)
				{
					try
					{
						if (flash.UseMaterialMode)
						{
							if (i < flash.OrigShared.Count)
							{
								val.sharedMaterials = flash.OrigShared[i];
							}
						}
						else
						{
							val.SetPropertyBlock((MaterialPropertyBlock)null);
						}
					}
					catch
					{
					}
				}
				if (!flash.UseMaterialMode || i >= flash.Instances.Count)
				{
					continue;
				}
				Material[] array = flash.Instances[i];
				foreach (Material val2 in array)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						Object.Destroy((Object)(object)val2);
					}
				}
			}
			flash.Renderers.Clear();
			flash.OrigShared.Clear();
			flash.Instances.Clear();
			v.Flash = null;
		}
	}
	internal class StaggerMeasurement
	{
		public Character Character;

		public float TriggeredAt;

		public float StartedAt = -1f;

		public bool SeenStaggering;
	}
	internal static class StaggerMeter
	{
		private static readonly List<StaggerMeasurement> Pending = new List<StaggerMeasurement>();

		private static readonly Dictionary<string, List<float>> Samples = new Dictionary<string, List<float>>();

		public static void Begin(Character c)
		{
			if (ParryBossesPlugin.CfgMeasure.Value && !((Object)(object)c == (Object)null) && !c.IsBoss())
			{
				Pending.RemoveAll((StaggerMeasurement p) => (Object)(object)p.Character == (Object)(object)c);
				Pending.Add(new StaggerMeasurement
				{
					Character = c,
					TriggeredAt = Time.time
				});
			}
		}

		public static void Update()
		{
			if (Pending.Count == 0)
			{
				return;
			}
			float time = Time.time;
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				StaggerMeasurement staggerMeasurement = Pending[num];
				Character character = staggerMeasurement.Character;
				if ((Object)(object)character == (Object)null)
				{
					Pending.RemoveAt(num);
				}
				else if (ParryBossesPlugin.RawAnimatorStaggering(character))
				{
					staggerMeasurement.SeenStaggering = true;
					if (staggerMeasurement.StartedAt < 0f)
					{
						staggerMeasurement.StartedAt = time;
					}
				}
				else if (staggerMeasurement.SeenStaggering)
				{
					float seconds = time - staggerMeasurement.StartedAt;
					Report(character.m_name, seconds, "动画僵直");
					Pending.RemoveAt(num);
				}
				else if (time - staggerMeasurement.TriggeredAt > 2f)
				{
					ParryBossesPlugin.Log.LogInfo((object)("[僵直时长] " + character.m_name + ":2 秒内动画未进入 stagger 状态(该角色没有 tag=stagger 的动画状态)"));
					Pending.RemoveAt(num);
				}
			}
		}

		private static void Report(string name, float seconds, string kind)
		{
			if (!Samples.TryGetValue(name, out var value))
			{
				value = new List<float>();
				Samples[name] = value;
			}
			value.Add(seconds);
			float num = 0f;
			foreach (float item in value)
			{
				num += item;
			}
			num /= (float)value.Count;
			ParryBossesPlugin.Log.LogInfo((object)($"[僵直时长] {name}:本次 {seconds:0.00}s({kind}),该角色已测 {value.Count} 次,平均 {num:0.00}s" + ((value.Count >= 3) ? ("," + Suggest()) : "")));
		}

		private static string Suggest()
		{
			List<float> list = new List<float>();
			foreach (KeyValuePair<string, List<float>> sample in Samples)
			{
				if (sample.Value.Count < 1)
				{
					continue;
				}
				float num = 0f;
				foreach (float item in sample.Value)
				{
					num += item;
				}
				list.Add(num / (float)sample.Value.Count);
			}
			if (list.Count == 0)
			{
				return "";
			}
			list.Sort();
			float num2 = list[list.Count / 2];
			return $"跨角色中位数 {num2:0.00}s → 建议 StaggerSeconds = {num2:0.0}";
		}

		public static void DumpStats()
		{
			if (Samples.Count == 0)
			{
				ParryBossesPlugin.Log.LogInfo((object)"[僵直时长统计] 还没有样本(用 F9 对几种小怪各试 2~3 次)");
				return;
			}
			ParryBossesPlugin.Log.LogInfo((object)"[僵直时长统计]");
			foreach (KeyValuePair<string, List<float>> sample in Samples)
			{
				float num = 0f;
				float num2 = float.MaxValue;
				float num3 = float.MinValue;
				foreach (float item in sample.Value)
				{
					num += item;
					if (item < num2)
					{
						num2 = item;
					}
					if (item > num3)
					{
						num3 = item;
					}
				}
				ParryBossesPlugin.Log.LogInfo((object)$"  {sample.Key}: 平均 {num / (float)sample.Value.Count:0.00}s({sample.Value.Count} 次,{num2:0.00}~{num3:0.00}s)");
			}
			ParryBossesPlugin.Log.LogInfo((object)("  " + Suggest()));
		}
	}
}