PPR.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("PPR")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PPR")]
[assembly: AssemblyTitle("PPR")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace PPR
{
	public class ConfigurationManagerAttributes
	{
		public int? Order;

		public string DispName;

		public string Category;

		public bool HideSettingName;

		public bool HideDefaultButton;

		public Action<ConfigEntryBase> CustomDrawer;
	}
	internal static class PPRConfigDisplayLocalization
	{
		private static readonly Regex KeyPattern = new Regex("^\\(([^)]+)\\)\\s*(.+)$");

		private static readonly Dictionary<string, string> SectionNameKo = new Dictionary<string, string>
		{
			["Perfect Parry"] = "퍼펙트 패링",
			["Perfect Parry - Damage Reduction"] = "퍼펙트 패링 - 피해 감소",
			["Perfect Parry - Effect"] = "퍼펙트 패링 - 이펙트",
			["Perfect Parry - Ranged"] = "퍼펙트 패링 - 원거리",
			["Shield Assignment"] = "방패 지정",
			["Auto Shield Equip"] = "방패 자동 장착",
			["Back Shield Guard"] = "후방 방패 가드",
			["Guard Durability"] = "가드 내구도",
			["Equip Motion Cancel"] = "장비 착용 모션 스킵",
			["Guard Sprint"] = "가드 질주",
			["Stagger Reduction"] = "스태거 감소",
			["Debug"] = "디버그"
		};

		private static readonly Dictionary<string, string> PrefixNameKo = new Dictionary<string, string>
		{
			["Enable"] = "사용",
			["Parry"] = "패링",
			["Counter"] = "반격",
			["Damage Reduction"] = "피해 감소",
			["Effect"] = "이펙트",
			["Reflect"] = "반사",
			["Input"] = "입력",
			["Display"] = "표시",
			["Guard"] = "가드",
			["Visual"] = "외형",
			["Armor"] = "방어구",
			["Blocker"] = "블로커",
			["Cancel"] = "취소",
			["Requirement"] = "요구조건",
			["Speed"] = "속도",
			["Classify"] = "분류",
			["Animation"] = "애니메이션",
			["Stamina"] = "스태미나",
			["Reduction"] = "감소",
			["Scope"] = "적용 범위"
		};

		private static readonly Dictionary<string, string> ItemNameKo = new Dictionary<string, string>
		{
			["Perfect Parry"] = "퍼펙트 패링",
			["Detection Window (sec)"] = "판정 시간(초)",
			["Input Window (sec)"] = "입력 시간(초)",
			["Animation Speed Multiplier"] = "애니메이션 속도 배율",
			["Damage Reduction"] = "피해 감소",
			["Reduction Rate (%)"] = "감소율(%)",
			["Head Effect"] = "머리 이펙트",
			["VFX Name"] = "이펙트 이름",
			["Offset X"] = "X 오프셋",
			["Offset Y"] = "Y 오프셋",
			["Offset Z"] = "Z 오프셋",
			["Custom Color Enabled"] = "커스텀 색상 사용",
			["Custom Color (Hex)"] = "커스텀 색상(Hex)",
			["Ranged No Stagger"] = "원거리 스태거 방지",
			["Projectile Reflect"] = "투사체 반사",
			["Perfect Parry Damage Multiplier"] = "퍼펙트 패링 데미지 배율",
			["Normal Parry Reflect"] = "일반 패링 반사",
			["Normal Parry Damage Multiplier"] = "일반 패링 데미지 배율",
			["Assign Key"] = "지정 키",
			["Show Role Icons"] = "역할 아이콘 표시",
			["Auto-Equip Assigned Shield"] = "지정 방패 자동 장착",
			["Back Shield Guard"] = "후방 방패 가드",
			["Rear Cone Half-Angle (deg)"] = "후방 판정각(도)",
			["Position Offset X"] = "위치 오프셋 X",
			["Position Offset Y"] = "위치 오프셋 Y",
			["Position Offset Z"] = "위치 오프셋 Z",
			["Rotation Offset X"] = "회전 오프셋 X",
			["Rotation Offset Y"] = "회전 오프셋 Y",
			["Rotation Offset Z"] = "회전 오프셋 Z",
			["No Armor Wear On Guard"] = "가드 시 방어구 내구도 보호",
			["No Durability Loss - Weapon"] = "무기 내구도 보호",
			["No Durability Loss - Tool"] = "도구 내구도 보호",
			["No Durability Loss - Shield"] = "방패 내구도 보호",
			["Equip Motion Cancel"] = "장비 착용 모션 스킵",
			["Shield"] = "방패",
			["Helmet"] = "투구",
			["Chest"] = "상의",
			["Legs"] = "하의",
			["Cape"] = "망토",
			["Utility"] = "유틸리티",
			["Trinket"] = "장신구",
			["Weapon"] = "무기",
			["Tool"] = "도구",
			["Hide/Show Hand Items (R)"] = "무기 숨기기/꺼내기(R)",
			["Guard Sprint"] = "가드 질주",
			["Min Blocking Skill"] = "최소 막기 스킬",
			["Buckler Penalty %"] = "버클러 감속 %",
			["Normal Shield Penalty %"] = "일반 방패 감속 %",
			["Tower Shield Penalty %"] = "대형 방패 감속 %",
			["Weapon Penalty %"] = "무기 감속 %",
			["Buckler Prefabs"] = "버클러 지정 목록",
			["Normal Prefabs"] = "일반 방패 지정 목록",
			["Tower Prefabs"] = "대형 방패 지정 목록",
			["Sync Leg Animation"] = "다리 모션 동기화",
			["Max Leg Speed-Up"] = "다리 모션 최대 배속",
			["Guard Sprint Stamina Multiplier"] = "가드 질주 스태미나 배율",
			["Stagger Reduction"] = "스태거 감소",
			["Max Reduction %"] = "최대 감소율(%)",
			["Apply To Unblocked Hits"] = "그냥 맞을 때도 적용",
			["Verbose Log"] = "상세 로그"
		};

		private static readonly HashSet<string> s_missingSections = new HashSet<string>();

		private static readonly HashSet<string> s_missingPrefixes = new HashSet<string>();

		private static readonly HashSet<string> s_missingItems = new HashSet<string>();

		private static readonly FieldInfo DescriptionBackingField = AccessTools.Field(typeof(ConfigEntryBase), "<Description>k__BackingField");

		private static bool s_descRefreshFailed;

		private static bool s_hooked;

		private static bool s_configManagerMissingLogged;

		public static void ApplyDisplayNames(ConfigFile cfg)
		{
			bool isKorean = PPRLocalizationManager.IsKorean;
			int num = 0;
			List<string> list = new List<string>();
			foreach (ConfigDefinition key in cfg.Keys)
			{
				ConfigEntryBase val = cfg[key];
				ConfigDescription description = val.Description;
				object[] array = ((description != null) ? description.Tags : null);
				if (array == null)
				{
					continue;
				}
				ConfigurationManagerAttributes configurationManagerAttributes = null;
				object[] array2 = array;
				foreach (object obj in array2)
				{
					if (obj is ConfigurationManagerAttributes configurationManagerAttributes2)
					{
						configurationManagerAttributes = configurationManagerAttributes2;
						break;
					}
				}
				if (configurationManagerAttributes != null)
				{
					configurationManagerAttributes.DispName = BuildDispName(key.Key, isKorean);
					configurationManagerAttributes.Category = BuildCategory(key.Section, isKorean);
					num++;
					if (list.Count < 3)
					{
						list.Add(configurationManagerAttributes.Category + " > " + configurationManagerAttributes.DispName);
					}
					TryRefreshDescriptionText(val, key.Key);
				}
			}
			Plugin.Log.LogInfo((object)($"[PPR:설정화면] 칸 제목/항목 이름 적용 — {num}개" + ((list.Count > 0) ? (", 예시: " + string.Join(" | ", list)) : "")));
			if (s_missingSections.Count > 0)
			{
				Plugin.Log.LogWarning((object)($"[PPR:설정화면] 한국어 칸 제목 번역 누락 {s_missingSections.Count}건 — " + string.Join(", ", s_missingSections) + " (영어 원문으로 표시됨)"));
			}
			if (s_missingPrefixes.Count > 0)
			{
				Plugin.Log.LogWarning((object)($"[PPR:설정화면] 한국어 접두어 번역 누락 {s_missingPrefixes.Count}건 — " + string.Join(", ", s_missingPrefixes) + " (영어 원문으로 표시됨)"));
			}
			if (s_missingItems.Count > 0)
			{
				Plugin.Log.LogWarning((object)($"[PPR:설정화면] 한국어 항목명 번역 누락 {s_missingItems.Count}건 — " + string.Join(", ", s_missingItems) + " (영어 원문으로 표시됨)"));
			}
		}

		private static string BuildCategory(string section, bool korean)
		{
			if (!korean)
			{
				return section;
			}
			if (SectionNameKo.TryGetValue(section, out var value))
			{
				return value;
			}
			s_missingSections.Add(section);
			return section;
		}

		private static string BuildDispName(string key, bool korean)
		{
			if (!korean)
			{
				return key;
			}
			Match match = KeyPattern.Match(key);
			if (!match.Success)
			{
				return key;
			}
			string value = match.Groups[1].Value;
			string value2 = match.Groups[2].Value;
			if (!PrefixNameKo.TryGetValue(value, out var value3))
			{
				value3 = value;
				s_missingPrefixes.Add(value);
			}
			if (!ItemNameKo.TryGetValue(value2, out var value4))
			{
				value4 = value2;
				s_missingItems.Add(value2);
			}
			return "(" + value3 + ") " + value4;
		}

		private static void TryRefreshDescriptionText(ConfigEntryBase entry, string key)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			if (s_descRefreshFailed)
			{
				return;
			}
			string text = PPRLocalizationManager.Desc(key);
			ConfigDescription description = entry.Description;
			if (description == null || description.Description == text)
			{
				return;
			}
			if (DescriptionBackingField == null)
			{
				s_descRefreshFailed = true;
				Plugin.Log.LogWarning((object)"[PPR:설정화면] 설명(Desc) 실시간 갱신 불가 — BepInEx 내부 필드를 못 찾음, 설명은 재시작 후에만 반영됩니다(항목 이름·칸 제목은 정상 작동).");
				return;
			}
			try
			{
				ConfigDescription value = new ConfigDescription(text, description.AcceptableValues, description.Tags);
				DescriptionBackingField.SetValue(entry, value);
			}
			catch (Exception ex)
			{
				s_descRefreshFailed = true;
				Plugin.Log.LogWarning((object)("[PPR:설정화면] 설명(Desc) 실시간 갱신 실패 — 설명은 재시작 후에만 반영됩니다(항목 이름·칸 제목은 정상 작동): " + ex.Message));
			}
		}

		public static void HookLanguageChangeEvent()
		{
			if (!s_hooked)
			{
				s_hooked = true;
				Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(RefreshForLanguageChange));
			}
		}

		public static void RefreshForLanguageChange()
		{
			PPRLocalizationManager.Init();
			ApplyDisplayNames(((BaseUnityPlugin)Plugin.Instance).Config);
			ForceConfigManagerRebuild();
		}

		private static void ForceConfigManagerRebuild()
		{
			try
			{
				Type type = AccessTools.TypeByName("ConfigurationManager.ConfigurationManager");
				if (type == null)
				{
					if (!s_configManagerMissingLogged)
					{
						s_configManagerMissingLogged = true;
						Plugin.Log.LogInfo((object)"[PPR:설정화면] ConfigurationManager 미설치 — 설정 창 즉시 갱신 생략(기능 자체엔 영향 없음, 설치돼 있다면 재시작 없이 바로 반영됩니다).");
					}
					return;
				}
				object obj = AccessTools.Field(type, "instance")?.GetValue(null);
				if (obj != null)
				{
					AccessTools.Method(type, "BuildSettingList", (Type[])null, (Type[])null)?.Invoke(obj, null);
					Plugin.Log.LogInfo((object)"[PPR:설정화면] 언어 변경 감지 — 칸 제목/항목 이름/설명 재계산 + 설정 목록 재구성 완료.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[PPR:설정화면] 설정 목록 강제 재구성 실패 — " + ex.Message));
			}
		}
	}
	internal static class PPRLocalizationManager
	{
		private static Dictionary<string, string> _strings = new Dictionary<string, string>();

		private static readonly HashSet<string> _missingDescKeys = new HashSet<string>();

		public static bool IsKorean { get; private set; }

		public static void Init()
		{
			string text = PlatformPrefs.GetString("language", "English");
			IsKorean = text == "Korean";
			string text2 = (IsKorean ? "ko" : "en");
			string text3 = "PPR.Localization." + text2 + ".json";
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			using (Stream stream = executingAssembly.GetManifestResourceStream(text3))
			{
				if (stream == null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("[PPR] Localization resource not found: " + text3));
					}
					return;
				}
				using StreamReader streamReader = new StreamReader(stream);
				_strings = ParseJson(streamReader.ReadToEnd());
			}
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[PPR] Localization loaded: {text2} ({_strings.Count} keys)");
			}
		}

		public static string Desc(string key)
		{
			if (_strings.TryGetValue(key, out var value))
			{
				return value;
			}
			_missingDescKeys.Add(key);
			return key;
		}

		public static void WarnMissingDescriptions()
		{
			if (_missingDescKeys.Count != 0)
			{
				Plugin.Log.LogWarning((object)($"[PPR:설정화면] 설명/문구 번역 누락 {_missingDescKeys.Count}건 — " + string.Join(", ", _missingDescKeys) + " (원문 키가 그대로 표시됨)"));
			}
		}

		private static Dictionary<string, string> ParseJson(string json)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			MatchCollection matchCollection = Regex.Matches(json, "\"((?:[^\"\\\\]|\\\\.)*?)\"[ \\t]*:[ \\t]*\"((?:[^\"\\\\]|\\\\.)*?)\"");
			foreach (Match item in matchCollection)
			{
				dictionary[item.Groups[1].Value] = Unescape(item.Groups[2].Value);
			}
			return dictionary;
		}

		private static string Unescape(string s)
		{
			return s.Replace("\\n", "\n").Replace("\\t", "\t").Replace("\\\"", "\"")
				.Replace("\\\\", "\\");
		}
	}
	[BepInPlugin("LJS.PPR", "PPR", "1.2.11")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "LJS.PPR";

		public const string PluginName = "PPR";

		public const string PluginVersion = "1.2.11";

		internal static ManualLogSource Log;

		internal static Plugin Instance;

		private readonly Harmony _harmony = new Harmony("LJS.PPR");

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			LogBuildInfo();
			PPRAccess.WarmUp();
			PPRConfig.Init(((BaseUnityPlugin)this).Config);
			PPRBackGuardPatch.HookConfig();
			ApplyHarmonyPatchesIsolated();
			Log.LogInfo((object)"PPR v1.2.11 loaded.");
		}

		private void ApplyHarmonyPatchesIsolated()
		{
			int num = 0;
			int num2 = 0;
			List<Type> list = (from t in AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly())
				where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0
				select t).ToList();
			foreach (Type item in list)
			{
				try
				{
					_harmony.CreateClassProcessor(item).Patch();
					num++;
				}
				catch (Exception arg)
				{
					num2++;
					Log.LogError((object)$"[PPR:패치실패] '{item.FullName}' 패치 적용 실패 — 이 기능만 비활성화된 채로 나머지는 계속 진행됨. {arg}");
				}
			}
			Log.LogInfo((object)$"[PPR:패치요약] 대상 클래스 {list.Count}개 — 성공 {num}개 / 실패 {num2}개");
			LogPatchedMethods();
		}

		private void LogPatchedMethods()
		{
			try
			{
				foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods())
				{
					Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod);
					if (patchInfo == null)
					{
						continue;
					}
					bool flag = patchInfo.Prefixes.Any((Patch p) => p.owner == "LJS.PPR");
					bool flag2 = patchInfo.Postfixes.Any((Patch p) => p.owner == "LJS.PPR");
					bool flag3 = patchInfo.Finalizers.Any((Patch p) => p.owner == "LJS.PPR");
					bool flag4 = patchInfo.Transpilers.Any((Patch p) => p.owner == "LJS.PPR");
					if (flag || flag2 || flag3 || flag4)
					{
						string text = string.Join("+", new string[4]
						{
							flag ? "Prefix" : null,
							flag2 ? "Postfix" : null,
							flag3 ? "Finalizer" : null,
							flag4 ? "Transpiler" : null
						}.Where((string s) => s != null));
						Log.LogInfo((object)("[PPR:패치목록] " + allPatchedMethod.DeclaringType?.Name + "." + allPatchedMethod.Name + " (" + text + ")"));
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[PPR] 패치 목록 조회 오류: " + ex.Message));
			}
		}

		private void LogBuildInfo()
		{
			try
			{
				string location = Assembly.GetExecutingAssembly().Location;
				DateTime lastWriteTime = File.GetLastWriteTime(location);
				Log.LogInfo((object)string.Format("[PPR:빌드정보] {0} — DLL 빌드 시각 {1:yyyy-MM-dd HH:mm:ss}", "1.2.11", lastWriteTime));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[PPR] 빌드 정보 로그 오류: " + ex.Message));
			}
		}
	}
	internal static class PPRAccess
	{
		private static int _successCount;

		private static int _failCount;

		private static readonly List<string> _failedNames;

		private static readonly Func<Humanoid, ItemData> _getCurrentBlocker;

		private static readonly Func<Character, float, Vector3, HitData, bool> _addStaggerDamage;

		private static readonly FieldRef<Character, SEMan> _semanRef;

		private static readonly FieldRef<VisEquipment, ZNetView> _visNetViewRef;

		private static readonly FieldRef<Humanoid, ItemData> _hiddenLeftItemRef;

		private static readonly FieldRef<Humanoid, ItemData> _hiddenRightItemRef;

		private static readonly FieldRef<ZNetScene, Dictionary<ZDO, ZNetView>> _znetInstancesRef;

		private static readonly Func<VisEquipment, int, int, Transform, bool, bool, int, GameObject> _attachItem;

		public static ItemData GetCurrentBlocker(Humanoid h)
		{
			return (_getCurrentBlocker != null) ? _getCurrentBlocker(h) : null;
		}

		public static bool AddStaggerDamage(Character c, float damage, Vector3 forceDirection, HitData hit)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return _addStaggerDamage != null && _addStaggerDamage(c, damage, forceDirection, hit);
		}

		public static SEMan GetSeman(Character c)
		{
			return (_semanRef != null) ? _semanRef.Invoke(c) : null;
		}

		public static ZNetView GetNetView(VisEquipment ve)
		{
			return (_visNetViewRef != null) ? _visNetViewRef.Invoke(ve) : null;
		}

		public static ItemData GetHiddenLeftItem(Humanoid h)
		{
			return (_hiddenLeftItemRef != null) ? _hiddenLeftItemRef.Invoke(h) : null;
		}

		public static ItemData GetHiddenRightItem(Humanoid h)
		{
			return (_hiddenRightItemRef != null) ? _hiddenRightItemRef.Invoke(h) : null;
		}

		public static Dictionary<ZDO, ZNetView> GetInstances(ZNetScene scene)
		{
			return (_znetInstancesRef != null) ? _znetInstancesRef.Invoke(scene) : null;
		}

		public static GameObject AttachItem(VisEquipment ve, int itemHash, int variant, Transform joint, bool enableEquipEffects, bool backAttach, int quality = 0)
		{
			return (_attachItem != null) ? _attachItem(ve, itemHash, variant, joint, enableEquipEffects, backAttach, quality) : null;
		}

		static PPRAccess()
		{
			_failedNames = new List<string>();
			_getCurrentBlocker = BuildMethodDelegate<Func<Humanoid, ItemData>>(typeof(Humanoid), "GetCurrentBlocker", null);
			_addStaggerDamage = BuildMethodDelegate<Func<Character, float, Vector3, HitData, bool>>(typeof(Character), "AddStaggerDamage", new Type[3]
			{
				typeof(float),
				typeof(Vector3),
				typeof(HitData)
			});
			_semanRef = BuildFieldRef<Character, SEMan>("m_seman");
			_visNetViewRef = BuildFieldRef<VisEquipment, ZNetView>("m_nview");
			_hiddenLeftItemRef = BuildFieldRef<Humanoid, ItemData>("m_hiddenLeftItem");
			_hiddenRightItemRef = BuildFieldRef<Humanoid, ItemData>("m_hiddenRightItem");
			_znetInstancesRef = BuildFieldRef<ZNetScene, Dictionary<ZDO, ZNetView>>("m_instances");
			_attachItem = BuildMethodDelegate<Func<VisEquipment, int, int, Transform, bool, bool, int, GameObject>>(typeof(VisEquipment), "AttachItem", null);
			if (_failCount == 0)
			{
				Plugin.Log.LogInfo((object)$"[PPR:접근검사] 성공 {_successCount} / 실패 0 — 비공개 멤버 접근자 전부 정상.");
			}
			else
			{
				Plugin.Log.LogError((object)string.Format("[PPR:접근검사] 성공 {0} / 실패 {1} — 실패 목록: {2} (해당 기능만 비활성화됨, 개별 실패 로그는 위 참고)", _successCount, _failCount, string.Join(", ", _failedNames)));
			}
		}

		public static void WarmUp()
		{
			RuntimeHelpers.RunClassConstructor(typeof(PPRAccess).TypeHandle);
		}

		private static TDelegate BuildMethodDelegate<TDelegate>(Type declaringType, string methodName, Type[] paramTypes) where TDelegate : Delegate
		{
			try
			{
				MethodInfo methodInfo = ((paramTypes != null) ? AccessTools.Method(declaringType, methodName, paramTypes, (Type[])null) : AccessTools.Method(declaringType, methodName, (Type[])null, (Type[])null));
				if (methodInfo == null)
				{
					throw new MissingMethodException(declaringType.FullName, methodName);
				}
				TDelegate result = AccessTools.MethodDelegate<TDelegate>(methodInfo, (object)null, true);
				_successCount++;
				return result;
			}
			catch (Exception ex)
			{
				string text = declaringType.FullName + "." + methodName;
				_failCount++;
				_failedNames.Add(text);
				Plugin.Log.LogError((object)("[PPR:접근검사] " + text + " 접근자 생성 실패 — 관련 기능만 비활성화됨: " + ex.Message));
				return null;
			}
		}

		private static FieldRef<T, F> BuildFieldRef<T, F>(string fieldName)
		{
			try
			{
				FieldRef<T, F> result = AccessTools.FieldRefAccess<T, F>(fieldName);
				_successCount++;
				return result;
			}
			catch (Exception ex)
			{
				string text = typeof(T).FullName + "." + fieldName;
				_failCount++;
				_failedNames.Add(text);
				Plugin.Log.LogError((object)("[PPR:접근검사] " + text + " 접근자 생성 실패 — 관련 기능만 비활성화됨: " + ex.Message));
				return null;
			}
		}
	}
	[HarmonyPatch]
	public static class PPRAutoEquipPatch
	{
		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		[HarmonyPostfix]
		public static void EquipItem_Postfix(Humanoid __instance, ItemData item, bool __result)
		{
			if (!__result || item == null)
			{
				return;
			}
			Player player = default(Player);
			ref Player reference = ref player;
			Humanoid obj = __instance;
			reference = (Player)(object)((obj is Player) ? obj : null);
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				PPRExceptionGuard.Run("AutoEquip.EquipItem", delegate
				{
					EquipItemLogic(__instance, player, item);
				});
			}
		}

		private static void EquipItemLogic(Humanoid __instance, Player player, ItemData item)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			if (PPREquipGuard.Suppressed)
			{
				PPRDebugLog.Log("자동 장착 건너뜀 — 억제 중(로그인 복원/R키 복귀/재귀 호출)");
			}
			else
			{
				if (!PPRConfig.AutoEquipShieldEnabled.Value || (int)item.m_shared.m_itemType != 3 || __instance.LeftItem != null)
				{
					return;
				}
				ItemData trackedShield = PPRShieldRole.TryGetAutoEquipShield(player);
				if (trackedShield == null)
				{
					PPRDebugLog.Log("자동 장착 건너뜀 — 자동 장착으로 지정된 방패 없음");
					return;
				}
				if (trackedShield.m_equipped)
				{
					PPRDebugLog.Log("자동 장착 건너뜀 — 추적 방패가 이미 장착 상태");
					return;
				}
				if (trackedShield.m_durability <= 0f)
				{
					PPRDebugLog.Log("자동 장착 건너뜀 — 추적 방패 내구도 0");
					return;
				}
				PPREquipGuard.RunSuppressed(delegate
				{
					__instance.EquipItem(trackedShield, true);
				});
				string[] obj = new string[5] { "자동 장착 실행 — ", null, null, null, null };
				GameObject dropPrefab = trackedShield.m_dropPrefab;
				obj[1] = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				obj[2] = " (한손무기 ";
				obj[3] = item.m_shared.m_name;
				obj[4] = " 장착에 대응)";
				PPRDebugLog.Log(string.Concat(obj));
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		[HarmonyPostfix]
		public static void Load_Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				PPRExceptionGuard.Run("AutoEquip.Load", delegate
				{
					LoadLogic(__instance);
				});
			}
		}

		private static void LoadLogic(Player __instance)
		{
			ItemData val = PPRShieldRole.ResolveCurrentHolder(__instance, PPRShieldRoleComponent.Back);
			ItemData val2 = PPRShieldRole.ResolveCurrentHolder(__instance, PPRShieldRoleComponent.AutoEquip);
			PPRDebugLog.Log("접속 — 등 방패 지정: " + (val?.m_shared?.m_name ?? "없음") + ", 자동 장착 지정: " + (val2?.m_shared?.m_name ?? "없음"));
		}

		[HarmonyPatch(typeof(Player), "EquipInventoryItems")]
		[HarmonyPrefix]
		public static void EquipInventoryItems_Prefix()
		{
			PPREquipGuard.PushSuppressed();
		}

		[HarmonyPatch(typeof(Player), "EquipInventoryItems")]
		[HarmonyFinalizer]
		public static Exception EquipInventoryItems_Finalizer(Exception __exception)
		{
			PPREquipGuard.PopSuppressed();
			return __exception;
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems")]
		[HarmonyPrefix]
		public static void ShowHandItems_Prefix()
		{
			PPREquipGuard.PushSuppressed();
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems")]
		[HarmonyFinalizer]
		public static Exception ShowHandItems_Finalizer(Exception __exception)
		{
			PPREquipGuard.PopSuppressed();
			return __exception;
		}
	}
	[HarmonyPatch]
	public static class PPRBackGuardPatch
	{
		private class PPRBackShieldVisual : MonoBehaviour
		{
			public int CurrentHash;

			public int CurrentVariant;

			public GameObject Instance;

			private void Awake()
			{
				_activeVisuals.Add(this);
			}

			private void OnDestroy()
			{
				_activeVisuals.Remove(this);
				if ((Object)(object)Instance != (Object)null)
				{
					Object.Destroy((Object)(object)Instance);
				}
			}
		}

		private static readonly int ZdoHashShield = StringExtensionMethods.GetStableHashCode("PPR_BackShieldHash");

		private static readonly int ZdoHashVariant = StringExtensionMethods.GetStableHashCode("PPR_BackShieldVariant");

		private static readonly HashSet<PPRBackShieldVisual> _activeVisuals = new HashSet<PPRBackShieldVisual>();

		public static void HookConfig()
		{
			EventHandler eventHandler = delegate
			{
				InvalidateAllVisuals();
			};
			PPRConfig.BackGuardOffsetX.SettingChanged += eventHandler;
			PPRConfig.BackGuardOffsetY.SettingChanged += eventHandler;
			PPRConfig.BackGuardOffsetZ.SettingChanged += eventHandler;
			PPRConfig.BackGuardRotationX.SettingChanged += eventHandler;
			PPRConfig.BackGuardRotationY.SettingChanged += eventHandler;
			PPRConfig.BackGuardRotationZ.SettingChanged += eventHandler;
		}

		private static void InvalidateAllVisuals()
		{
			foreach (PPRBackShieldVisual activeVisual in _activeVisuals)
			{
				if ((Object)(object)activeVisual != (Object)null)
				{
					activeVisual.CurrentHash = int.MinValue;
				}
			}
			PPRDebugLog.Log("등 방패 오프셋 설정 변경 — 기존 인스턴스 재적용 예약됨");
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		[HarmonyPrefix]
		public static void RPC_Damage_Prefix(Character __instance, HitData hit)
		{
			PPRBackGuardState.Clear();
			if (!PPRConfig.BackGuardEnabled.Value)
			{
				return;
			}
			Player player = default(Player);
			ref Player reference = ref player;
			Character obj = __instance;
			reference = (Player)(object)((obj is Player) ? obj : null);
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && hit != null)
			{
				PPRExceptionGuard.Run("BackGuard.RPC_Damage", delegate
				{
					ArmBackGuardIfEligible(__instance, player, hit);
				});
			}
		}

		private static void ArmBackGuardIfEligible(Character __instance, Player player, HitData hit)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			if (!hit.m_blockable)
			{
				PPRDebugLog.Log("후방 가드 미무장 — hit.m_blockable=false");
				return;
			}
			if (hit.m_dodgeable && __instance.IsDodgeInvincible())
			{
				PPRDebugLog.Log("후방 가드 미무장 — 구르기 무적 중(이 히트는 곧 무시됨)");
				return;
			}
			if (__instance.IsStaggering())
			{
				PPRDebugLog.Log("후방 가드 미무장 — 스태거 중");
				return;
			}
			if (!PPRShieldRole.IsOnBack(player, out var shield))
			{
				PPRDebugLog.Log("후방 가드 미무장 — 등 대기 상태 아님");
				return;
			}
			if (shield.m_durability <= 0f)
			{
				PPRDebugLog.Log("후방 가드 미무장 — 추적 방패 내구도 0");
				return;
			}
			Vector3 forward = ((Component)__instance).transform.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f)
			{
				PPRDebugLog.Log("후방 가드 미무장 — forward 벡터가 0");
				return;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 dir = hit.m_dir;
			dir.y = 0f;
			if (((Vector3)(ref dir)).sqrMagnitude < 0.0001f)
			{
				PPRDebugLog.Log("후방 가드 미무장 — hit.m_dir 벡터가 0");
				return;
			}
			((Vector3)(ref dir)).Normalize();
			float num = Vector3.Dot(dir, forward);
			float num2 = Mathf.Cos(PPRConfig.BackGuardHalfAngle.Value * ((float)Math.PI / 180f));
			if (num < num2)
			{
				PPRDebugLog.Log($"후방 가드 미무장 — 후방 원뿔 밖(dot={num:F2}, 기준={num2:F2})");
				return;
			}
			PPRBackGuardState.Arm(__instance, shield);
			object arg = num;
			object arg2 = num2;
			GameObject dropPrefab = shield.m_dropPrefab;
			PPRDebugLog.Log($"후방 가드 무장 — dot={arg:F2} >= 기준={arg2:F2}, 방패={((dropPrefab != null) ? ((Object)dropPrefab).name : null)}");
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		[HarmonyFinalizer]
		public static Exception RPC_Damage_Finalizer(Exception __exception)
		{
			PPRBackGuardState.Clear();
			return __exception;
		}

		[HarmonyPatch(typeof(Humanoid), "IsBlocking")]
		[HarmonyPostfix]
		public static void IsBlocking_Postfix(Humanoid __instance, ref bool __result)
		{
			if (!__result && PPRBackGuardState.IsArmedFor((Character)(object)__instance))
			{
				__result = true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		public static bool BlockAttack_Prefix(Humanoid __instance, HitData hit, Character attacker, ref bool __result)
		{
			Character character = (Character)(object)__instance;
			if (!PPRBackGuardState.IsArmedFor(character))
			{
				return true;
			}
			ItemData shield = PPRBackGuardState.ArmedShield;
			PPRBackGuardState.Clear();
			PPRBackGuardState.MarkHandledByBackGuard();
			PPRExceptionGuard.Run("BackGuard.BlockAttack", delegate
			{
				ApplyBackGuardBlock(__instance, shield, hit, attacker);
			});
			__result = true;
			return false;
		}

		private static void ApplyBackGuardBlock(Humanoid humanoid, ItemData shield, HitData hit, Character attacker)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			float skillFactor = ((Character)humanoid).GetSkillFactor((SkillType)6);
			float blockPower = shield.GetBlockPower(skillFactor);
			DamageTypes val = ((DamageTypes)(ref hit.m_damage)).Clone();
			((DamageTypes)(ref val)).ApplyArmor(blockPower);
			float totalBlockableDamage = hit.GetTotalBlockableDamage();
			float totalBlockableDamage2 = ((DamageTypes)(ref val)).GetTotalBlockableDamage();
			float num = totalBlockableDamage - totalBlockableDamage2;
			float num2 = Mathf.Clamp01((blockPower <= 0f) ? 0f : (num / blockPower));
			float num3 = humanoid.m_blockStaminaDrain * num2;
			num3 += num3 * ((Character)humanoid).GetEquipmentBlockStaminaModifier();
			SEMan seman = PPRAccess.GetSeman((Character)(object)humanoid);
			if (seman != null)
			{
				seman.ModifyBlockStaminaUsage(num3, ref num3, true);
			}
			((Character)humanoid).UseStamina(num3);
			float totalStaggerDamage = ((DamageTypes)(ref val)).GetTotalStaggerDamage();
			bool flag = PPRAccess.AddStaggerDamage((Character)(object)humanoid, totalStaggerDamage, hit.m_dir, hit);
			bool flag2 = ((Character)humanoid).HaveStamina(0f) && !flag;
			float totalDamage = hit.GetTotalDamage();
			if (flag2)
			{
				hit.m_statusEffectHash = 0;
				hit.BlockDamage(num);
				hit.m_pushForce *= num2;
				DamageText.instance.ShowText((TextType)6, hit.m_point + Vector3.up * 0.5f, num, false);
			}
			float durability = shield.m_durability;
			if (shield.m_shared.m_useDurability)
			{
				float num4 = shield.m_shared.m_useDurabilityDrain * ((blockPower <= 0f) ? 0f : (totalBlockableDamage / blockPower));
				shield.m_durability -= num4;
			}
			((Character)humanoid).RaiseSkill((SkillType)6, 1f);
			shield.m_shared.m_blockEffect.Create(hit.m_point, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			if (flag2)
			{
				PPRGuardDurabilityState.MarkGuardSucceeded();
				if (PPRGuardDurabilityPatch.ShouldProtectBlocker(shield) && shield.m_durability < durability)
				{
					shield.m_durability = durability;
					PPRDebugLog.Log("PPR-GUARD", "블로커 내구도 복원(후방) — " + shield.m_shared.m_name + "(Shield)");
				}
			}
			float totalDamage2 = hit.GetTotalDamage();
			PPRDebugLog.Log($"후방 가드 발동 — blockPower={blockPower:F1}, dmg {totalDamage:F1}->{totalDamage2:F1}, stamina={num3:F1}, negated={flag2}");
		}

		[HarmonyPatch(typeof(VisEquipment), "UpdateVisuals")]
		[HarmonyPostfix]
		public static void UpdateVisuals_Postfix(VisEquipment __instance)
		{
			if (!__instance.m_isPlayer)
			{
				return;
			}
			Player player = ((Component)__instance).GetComponent<Player>();
			if (!((Object)(object)player == (Object)null))
			{
				PPRExceptionGuard.Run("BackGuard.UpdateVisuals", delegate
				{
					UpdateVisualsLogic(__instance, player);
				});
			}
		}

		private static void UpdateVisualsLogic(VisEquipment __instance, Player player)
		{
			ZNetView netView = PPRAccess.GetNetView(__instance);
			ZDO val = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null);
			if (val == null)
			{
				return;
			}
			if (!PPRConfig.BackGuardEnabled.Value)
			{
				if (netView.IsOwner() && val.GetInt(ZdoHashShield, 0) != 0)
				{
					val.Set(ZdoHashShield, 0, false);
				}
				ReconcileVisual(__instance, 0, 0);
				return;
			}
			if (netView.IsOwner())
			{
				ItemData shield;
				bool flag = PPRShieldRole.IsOnBack(player, out shield);
				if (flag && PPRAccess.GetHiddenLeftItem((Humanoid)(object)player) == shield)
				{
					flag = false;
				}
				int num = ((flag && (Object)(object)shield.m_dropPrefab != (Object)null) ? StringExtensionMethods.GetStableHashCode(((Object)shield.m_dropPrefab).name) : 0);
				int num2 = (flag ? shield.m_variant : 0);
				if (val.GetInt(ZdoHashShield, 0) != num)
				{
					val.Set(ZdoHashShield, num, false);
				}
				if (val.GetInt(ZdoHashVariant, 0) != num2)
				{
					val.Set(ZdoHashVariant, num2, false);
				}
			}
			int hash = val.GetInt(ZdoHashShield, 0);
			int variant = val.GetInt(ZdoHashVariant, 0);
			ReconcileVisual(__instance, hash, variant);
		}

		private static void ReconcileVisual(VisEquipment visEq, int hash, int variant)
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			PPRBackShieldVisual pPRBackShieldVisual = ((Component)visEq).GetComponent<PPRBackShieldVisual>();
			if ((Object)(object)pPRBackShieldVisual == (Object)null)
			{
				if (hash == 0)
				{
					return;
				}
				pPRBackShieldVisual = ((Component)visEq).gameObject.AddComponent<PPRBackShieldVisual>();
			}
			if (pPRBackShieldVisual.CurrentHash == hash && pPRBackShieldVisual.CurrentVariant == variant)
			{
				return;
			}
			pPRBackShieldVisual.CurrentHash = hash;
			pPRBackShieldVisual.CurrentVariant = variant;
			if ((Object)(object)pPRBackShieldVisual.Instance != (Object)null)
			{
				Object.Destroy((Object)(object)pPRBackShieldVisual.Instance);
				pPRBackShieldVisual.Instance = null;
				PPRDebugLog.Log("등 방패 인스턴스 제거");
			}
			if (hash != 0 && !((Object)(object)visEq.m_backShield == (Object)null))
			{
				GameObject val = PPRAccess.AttachItem(visEq, hash, variant, visEq.m_backShield, enableEquipEffects: false, backAttach: true);
				if ((Object)(object)val == (Object)null)
				{
					PPRDebugLog.Warn($"등 방패 인스턴스 생성 실패 — AttachItem이 null 반환(hash={hash}, variant={variant}, m_backShield={(Object)(object)visEq.m_backShield != (Object)null})");
					return;
				}
				Transform transform = val.transform;
				transform.localPosition += new Vector3(PPRConfig.BackGuardOffsetX.Value, PPRConfig.BackGuardOffsetY.Value, PPRConfig.BackGuardOffsetZ.Value);
				Transform transform2 = val.transform;
				transform2.localRotation *= Quaternion.Euler(PPRConfig.BackGuardRotationX.Value, PPRConfig.BackGuardRotationY.Value, PPRConfig.BackGuardRotationZ.Value);
				pPRBackShieldVisual.Instance = val;
				PPRDebugLog.Log($"등 방패 인스턴스 생성 완료 (hash={hash}, variant={variant})");
			}
		}
	}
	internal static class PPRBackGuardState
	{
		private static Character _armedFor;

		private static ItemData _armedShield;

		private static bool _handledByBackGuard;

		public static ItemData ArmedShield => _armedShield;

		public static void Arm(Character character, ItemData shield)
		{
			_armedFor = character;
			_armedShield = shield;
		}

		public static bool IsArmedFor(Character character)
		{
			return character != null && _armedFor == character;
		}

		public static void MarkHandledByBackGuard()
		{
			_handledByBackGuard = true;
		}

		public static bool ConsumeHandledByBackGuard()
		{
			bool handledByBackGuard = _handledByBackGuard;
			_handledByBackGuard = false;
			return handledByBackGuard;
		}

		public static void Clear()
		{
			_armedFor = null;
			_armedShield = null;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
	public static class PPRBlockPatch
	{
		private const float VanillaPerfectBlockInterval = 0.25f;

		[HarmonyPostfix]
		public static void Postfix(Humanoid __instance, HitData hit, Character attacker, bool __result, float ___m_blockTimer)
		{
			if (!__result || PPRBackGuardState.ConsumeHandledByBackGuard())
			{
				return;
			}
			Player val = default(Player);
			ref Player reference = ref val;
			Humanoid obj = __instance;
			reference = (Player)(object)((obj is Player) ? obj : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
			{
				PPRExceptionGuard.Run("PerfectParry.BlockAttack", delegate
				{
					BlockAttackLogic(__instance, hit, attacker, val, ___m_blockTimer);
				});
			}
		}

		private static void BlockAttackLogic(Humanoid __instance, HitData hit, Character attacker, Player val, float blockTimer)
		{
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Invalid comparison between Unknown and I4
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Invalid comparison between Unknown and I4
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Invalid comparison between Unknown and I4
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			bool flag = blockTimer >= 0f;
			ItemData currentBlocker = PPRAccess.GetCurrentBlocker(__instance);
			bool flag2 = currentBlocker != null && currentBlocker.m_shared.m_timedBlockBonus > 1f;
			bool flag3 = PPRConfig.Enable.Value && flag && blockTimer < PPRConfig.ParryWindow.Value && flag2 && ((Character)__instance).HaveStamina(0f) && !((Character)__instance).IsStaggering();
			bool flag4 = blockTimer < 0.25f;
			string text = (flag3 ? "퍼펙트" : ((flag && flag4) ? "일반" : "가드"));
			Plugin.Log.LogInfo((object)$"[PPR-DIAG] raw blockTimer={blockTimer:F3}s, perfectThreshold={PPRConfig.ParryWindow.Value:F2}s, normalThreshold={0.25f:F2}s, 판정={text} (hasPerfectBlockCapability={flag2}, ranged={hit.m_ranged})");
			if (flag3)
			{
				ItemData currentWeapon = __instance.GetCurrentWeapon();
				bool flag5 = false;
				string text2 = "무기 없음";
				if (currentWeapon != null)
				{
					ItemType itemType = currentWeapon.m_shared.m_itemType;
					bool flag6 = (int)itemType == 3 || (int)itemType == 14 || (int)itemType == 22;
					bool flag7 = currentWeapon.HaveSecondaryAttack();
					if (flag6 && flag7)
					{
						PPRState.ActivateParry(val);
						PPRState.SpawnParryVFX(val);
						flag5 = true;
					}
					else
					{
						text2 = $"itemType={itemType}(eligible={flag6}), hasSecondaryAttack={flag7}";
					}
				}
				Plugin.Log.LogInfo((object)string.Format("[PPR] 퍼펙트 패링 (blockTimer={0:F3}s, 카운터윈도우={1})", blockTimer, flag5 ? "활성(데미지감소 가능)" : ("비활성(" + text2 + ")")));
				if (hit.m_ranged && PPRConfig.ProjectileReflect.Value)
				{
					PPRProjectileReflectPatch.TryReflect(__instance, hit, attacker, isPerfect: true);
				}
			}
			else if (flag && hit.m_ranged)
			{
				if (blockTimer < 0.25f && PPRConfig.NormalParryReflect.Value)
				{
					Plugin.Log.LogInfo((object)$"[PPR] 패링 성공(반사) (blockTimer={blockTimer:F3}s)");
					PPRProjectileReflectPatch.TryReflect(__instance, hit, attacker, isPerfect: false);
				}
				else
				{
					Plugin.Log.LogInfo((object)$"[PPR] 가드만(반사 없음) (blockTimer={blockTimer:F3}s)");
				}
			}
		}
	}
	public static class PPRConfig
	{
		private const string SEC = "Perfect Parry";

		private const string SEC_DMG = "Perfect Parry - Damage Reduction";

		private const string SEC_VFX = "Perfect Parry - Effect";

		private const string SEC_RANGED = "Perfect Parry - Ranged";

		private const string SEC_SHIELDASSIGN = "Shield Assignment";

		private const string SEC_AUTOSHIELD = "Auto Shield Equip";

		private const string SEC_BACKGUARD = "Back Shield Guard";

		private const string SEC_GUARDDUR = "Guard Durability";

		private const string SEC_EQUIPCANCEL = "Equip Motion Cancel";

		private const string SEC_GUARDSPRINT = "Guard Sprint";

		private const string SEC_STAGGERREDUCTION = "Stagger Reduction";

		private const string SEC_DEBUG = "Debug";

		public static ConfigEntry<bool> Enable;

		public static ConfigEntry<float> ParryWindow;

		public static ConfigEntry<float> CounterWindow;

		public static ConfigEntry<float> CounterSpeedMultiplier;

		public static ConfigEntry<bool> DamageReductionEnabled;

		public static ConfigEntry<float> DamageReductionPercent;

		public static ConfigEntry<bool> VFXEnabled;

		public static ConfigEntry<string> VFXPrefab;

		public static ConfigEntry<float> VFXOffsetX;

		public static ConfigEntry<float> VFXOffsetY;

		public static ConfigEntry<float> VFXOffsetZ;

		public static ConfigEntry<bool> VFXColorEnabled;

		public static ConfigEntry<string> VFXColorHex;

		public static ConfigEntry<bool> RangedNoStagger;

		public static ConfigEntry<bool> ProjectileReflect;

		public static ConfigEntry<float> ProjectileReflectDamageMult;

		public static ConfigEntry<bool> NormalParryReflect;

		public static ConfigEntry<float> NormalParryReflectDamageMult;

		public static ConfigEntry<KeyCode> ShieldAssignKey;

		public static ConfigEntry<bool> ShieldAssignIconsEnabled;

		public static ConfigEntry<bool> AutoEquipShieldEnabled;

		public static ConfigEntry<bool> BackGuardEnabled;

		public static ConfigEntry<float> BackGuardHalfAngle;

		public static ConfigEntry<float> BackGuardOffsetX;

		public static ConfigEntry<float> BackGuardOffsetY;

		public static ConfigEntry<float> BackGuardOffsetZ;

		public static ConfigEntry<float> BackGuardRotationX;

		public static ConfigEntry<float> BackGuardRotationY;

		public static ConfigEntry<float> BackGuardRotationZ;

		public static ConfigEntry<bool> NoArmorWearOnGuard;

		public static ConfigEntry<bool> NoDurabilityLossWeapon;

		public static ConfigEntry<bool> NoDurabilityLossTool;

		public static ConfigEntry<bool> NoDurabilityLossShield;

		public static ConfigEntry<bool> EquipMotionCancelEnabled;

		public static ConfigEntry<bool> EquipCancelShield;

		public static ConfigEntry<bool> EquipCancelHelmet;

		public static ConfigEntry<bool> EquipCancelChest;

		public static ConfigEntry<bool> EquipCancelLegs;

		public static ConfigEntry<bool> EquipCancelCape;

		public static ConfigEntry<bool> EquipCancelUtility;

		public static ConfigEntry<bool> EquipCancelTrinket;

		public static ConfigEntry<bool> EquipCancelWeapon;

		public static ConfigEntry<bool> EquipCancelTool;

		public static ConfigEntry<bool> EquipCancelHideShow;

		public static ConfigEntry<bool> GuardSprintEnabled;

		public static ConfigEntry<float> GuardSprintMinBlockingSkill;

		public static ConfigEntry<float> GuardSprintBucklerPenaltyPercent;

		public static ConfigEntry<float> GuardSprintNormalShieldPenaltyPercent;

		public static ConfigEntry<float> GuardSprintTowerPenaltyPercent;

		public static ConfigEntry<float> GuardSprintWeaponPenaltyPercent;

		public static ConfigEntry<string> GuardSprintBucklerPrefabs;

		public static ConfigEntry<string> GuardSprintNormalPrefabs;

		public static ConfigEntry<string> GuardSprintTowerPrefabs;

		public static ConfigEntry<bool> GuardSprintSyncLegAnimation;

		public static ConfigEntry<float> GuardSprintMaxLegSpeedUp;

		public static ConfigEntry<float> GuardSprintStaminaMultiplier;

		public static ConfigEntry<bool> StaggerReductionEnabled;

		public static ConfigEntry<float> StaggerReductionMaxPercent;

		public static ConfigEntry<bool> StaggerReductionApplyToUnblockedHits;

		public static ConfigEntry<bool> VerboseLog;

		private static readonly PropertyInfo OrphanedEntriesProp = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries");

		public static void Init(ConfigFile cfg)
		{
			PPRLocalizationManager.Init();
			Enable = BindSetting(cfg, "Perfect Parry", "(Enable) Perfect Parry", defaultValue: true);
			ParryWindow = BindSetting(cfg, "Perfect Parry", "(Parry) Detection Window (sec)", 0.1f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.24f));
			CounterWindow = BindSetting(cfg, "Perfect Parry", "(Counter) Input Window (sec)", 1f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f));
			CounterSpeedMultiplier = BindSetting(cfg, "Perfect Parry", "(Counter) Animation Speed Multiplier", 2f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f));
			DamageReductionEnabled = BindSetting(cfg, "Perfect Parry - Damage Reduction", "(Enable) Damage Reduction", defaultValue: true);
			DamageReductionPercent = BindSetting(cfg, "Perfect Parry - Damage Reduction", "(Damage Reduction) Reduction Rate (%)", 50f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			VFXEnabled = BindSetting(cfg, "Perfect Parry - Effect", "(Enable) Head Effect", defaultValue: true);
			VFXPrefab = BindSetting(cfg, "Perfect Parry - Effect", "(Effect) VFX Name", "vfx_blocked");
			VFXOffsetX = BindSetting(cfg, "Perfect Parry - Effect", "(Effect) Offset X", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-5f, 5f));
			VFXOffsetY = BindSetting(cfg, "Perfect Parry - Effect", "(Effect) Offset Y", 1.25f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 8f));
			VFXOffsetZ = BindSetting(cfg, "Perfect Parry - Effect", "(Effect) Offset Z", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-5f, 5f));
			VFXColorEnabled = BindSetting(cfg, "Perfect Parry - Effect", "(Effect) Custom Color Enabled", defaultValue: true);
			VFXColorHex = BindSetting(cfg, "Perfect Parry - Effect", "(Effect) Custom Color (Hex)", "#4DB8FF");
			RangedNoStagger = BindSetting(cfg, "Perfect Parry - Ranged", "(Enable) Ranged No Stagger", defaultValue: true);
			ProjectileReflect = BindSetting(cfg, "Perfect Parry - Ranged", "(Enable) Projectile Reflect", defaultValue: true);
			ProjectileReflectDamageMult = BindSetting(cfg, "Perfect Parry - Ranged", "(Reflect) Perfect Parry Damage Multiplier", 3f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f));
			NormalParryReflect = BindSetting(cfg, "Perfect Parry - Ranged", "(Enable) Normal Parry Reflect", defaultValue: true);
			NormalParryReflectDamageMult = BindSetting(cfg, "Perfect Parry - Ranged", "(Reflect) Normal Parry Damage Multiplier", 2f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f));
			ShieldAssignKey = BindSetting<KeyCode>(cfg, "Shield Assignment", "(Input) Assign Key", (KeyCode)325);
			ShieldAssignIconsEnabled = BindSetting(cfg, "Shield Assignment", "(Display) Show Role Icons", defaultValue: true);
			AutoEquipShieldEnabled = BindSetting(cfg, "Auto Shield Equip", "(Enable) Auto-Equip Assigned Shield", defaultValue: true);
			BackGuardEnabled = BindSetting(cfg, "Back Shield Guard", "(Enable) Back Shield Guard", defaultValue: true);
			BackGuardHalfAngle = BindSetting(cfg, "Back Shield Guard", "(Guard) Rear Cone Half-Angle (deg)", 60f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 90f));
			BackGuardOffsetX = BindSetting(cfg, "Back Shield Guard", "(Visual) Position Offset X", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f));
			BackGuardOffsetY = BindSetting(cfg, "Back Shield Guard", "(Visual) Position Offset Y", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f));
			BackGuardOffsetZ = BindSetting(cfg, "Back Shield Guard", "(Visual) Position Offset Z", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f));
			BackGuardRotationX = BindSetting(cfg, "Back Shield Guard", "(Visual) Rotation Offset X", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f));
			BackGuardRotationY = BindSetting(cfg, "Back Shield Guard", "(Visual) Rotation Offset Y", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f));
			BackGuardRotationZ = BindSetting(cfg, "Back Shield Guard", "(Visual) Rotation Offset Z", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f));
			NoArmorWearOnGuard = BindSetting(cfg, "Guard Durability", "(Armor) No Armor Wear On Guard", defaultValue: true);
			NoDurabilityLossWeapon = BindSetting(cfg, "Guard Durability", "(Blocker) No Durability Loss - Weapon", defaultValue: true);
			NoDurabilityLossTool = BindSetting(cfg, "Guard Durability", "(Blocker) No Durability Loss - Tool", defaultValue: true);
			NoDurabilityLossShield = BindSetting(cfg, "Guard Durability", "(Blocker) No Durability Loss - Shield", defaultValue: false);
			EquipMotionCancelEnabled = BindSetting(cfg, "Equip Motion Cancel", "(Enable) Equip Motion Cancel", defaultValue: true);
			EquipCancelShield = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Shield", defaultValue: true);
			EquipCancelHelmet = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Helmet", defaultValue: true);
			EquipCancelChest = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Chest", defaultValue: true);
			EquipCancelLegs = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Legs", defaultValue: true);
			EquipCancelCape = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Cape", defaultValue: true);
			EquipCancelUtility = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Utility", defaultValue: true);
			EquipCancelTrinket = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Trinket", defaultValue: true);
			EquipCancelWeapon = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Weapon", defaultValue: true);
			EquipCancelTool = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Tool", defaultValue: true);
			EquipCancelHideShow = BindSetting(cfg, "Equip Motion Cancel", "(Cancel) Hide/Show Hand Items (R)", defaultValue: true);
			GuardSprintEnabled = BindSetting(cfg, "Guard Sprint", "(Enable) Guard Sprint", defaultValue: true);
			GuardSprintMinBlockingSkill = BindSetting(cfg, "Guard Sprint", "(Requirement) Min Blocking Skill", 20f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			GuardSprintBucklerPenaltyPercent = BindSetting(cfg, "Guard Sprint", "(Speed) Buckler Penalty %", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			GuardSprintNormalShieldPenaltyPercent = BindSetting(cfg, "Guard Sprint", "(Speed) Normal Shield Penalty %", 10f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			GuardSprintTowerPenaltyPercent = BindSetting(cfg, "Guard Sprint", "(Speed) Tower Shield Penalty %", 20f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			GuardSprintWeaponPenaltyPercent = BindSetting(cfg, "Guard Sprint", "(Speed) Weapon Penalty %", 0f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			GuardSprintBucklerPrefabs = BindSetting(cfg, "Guard Sprint", "(Classify) Buckler Prefabs", "");
			GuardSprintNormalPrefabs = BindSetting(cfg, "Guard Sprint", "(Classify) Normal Prefabs", "");
			GuardSprintTowerPrefabs = BindSetting(cfg, "Guard Sprint", "(Classify) Tower Prefabs", "");
			GuardSprintSyncLegAnimation = BindSetting(cfg, "Guard Sprint", "(Animation) Sync Leg Animation", defaultValue: true);
			GuardSprintMaxLegSpeedUp = BindSetting(cfg, "Guard Sprint", "(Animation) Max Leg Speed-Up", 2f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f));
			GuardSprintStaminaMultiplier = BindSetting(cfg, "Guard Sprint", "(Stamina) Guard Sprint Stamina Multiplier", 1.1f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f));
			StaggerReductionEnabled = BindSetting(cfg, "Stagger Reduction", "(Enable) Stagger Reduction", defaultValue: true);
			StaggerReductionMaxPercent = BindSetting(cfg, "Stagger Reduction", "(Reduction) Max Reduction %", 50f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			StaggerReductionApplyToUnblockedHits = BindSetting(cfg, "Stagger Reduction", "(Scope) Apply To Unblocked Hits", defaultValue: false);
			VerboseLog = BindSetting(cfg, "Debug", "(Enable) Verbose Log", defaultValue: false);
			CleanupOrphanedEntries(cfg);
			PPRConfigDisplayLocalization.ApplyDisplayNames(cfg);
			PPRConfigDisplayLocalization.HookLanguageChangeEvent();
			PPRLocalizationManager.WarnMissingDescriptions();
		}

		private static ConfigEntry<T> BindSetting<T>(ConfigFile cfg, string section, string key, T defaultValue, AcceptableValueBase acceptableValues = null)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			string text = PPRLocalizationManager.Desc(key);
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
			return cfg.Bind<T>(section, key, defaultValue, new ConfigDescription(text, acceptableValues, new object[1] { configurationManagerAttributes }));
		}

		private static void CleanupOrphanedEntries(ConfigFile cfg)
		{
			try
			{
				if (OrphanedEntriesProp?.GetValue(cfg) is IDictionary { Count: not 0, Count: var count } dictionary)
				{
					dictionary.Clear();
					cfg.Save();
					Plugin.Log.LogInfo((object)$"[PPR:설정정리] 연결되지 않은 옛 설정 항목 {count}개 삭제 후 저장.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[PPR] 옛 설정 정리 실패(무해, 옛 항목이 파일에 남을 수 있음): " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	public static class PPRDamagePatch
	{
		[HarmonyPrefix]
		public static void Prefix(Character __instance, HitData hit)
		{
			if (!PPRConfig.Enable.Value || !PPRConfig.DamageReductionEnabled.Value)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && hit != null)
			{
				PPRExceptionGuard.Run("DamageReduction.ApplyDamage", delegate
				{
					ApplyDamageReduction(val, hit);
				});
			}
		}

		private static void ApplyDamageReduction(Player val, HitData hit)
		{
			if (PPRState.IsParryWindowActive(val))
			{
				float totalDamage = ((DamageTypes)(ref hit.m_damage)).GetTotalDamage();
				float num = 1f - PPRConfig.DamageReductionPercent.Value / 100f;
				((DamageTypes)(ref hit.m_damage)).Modify(num);
				float totalDamage2 = ((DamageTypes)(ref hit.m_damage)).GetTotalDamage();
				Plugin.Log.LogInfo((object)("[PPR] 데미지 감소 적용 (카운터윈도우, 퍼펙트패링) - " + $"배율={num:F2}x({PPRConfig.DamageReductionPercent.Value}%), 적용전={totalDamage:F1} → 적용후={totalDamage2:F1}"));
			}
		}
	}
	internal static class PPRDebugLog
	{
		public static void Log(string message)
		{
			Log("PPR-SHIELD", message);
		}

		public static void Warn(string message)
		{
			Warn("PPR-SHIELD", message);
		}

		public static void Log(string tag, string message)
		{
			if (PPRConfig.VerboseLog != null && PPRConfig.VerboseLog.Value)
			{
				Plugin.Log.LogInfo((object)("[" + tag + "] " + message));
			}
		}

		public static void Warn(string tag, string message)
		{
			if (PPRConfig.VerboseLog != null && PPRConfig.VerboseLog.Value)
			{
				Plugin.Log.LogWarning((object)("[" + tag + "] " + message));
			}
		}
	}
	internal static class PPREquipGuard
	{
		private static int _depth;

		public static bool Suppressed => _depth > 0;

		public static void PushSuppressed()
		{
			_depth++;
		}

		public static void PopSuppressed()
		{
			if (_depth <= 0)
			{
				Plugin.Log.LogWarning((object)"[PPR-SHIELD] PPREquipGuard.PopSuppressed 호출 시 깊이가 이미 0 — Push/Pop 짝이 맞지 않음(예외 경로에서 Pop이 빠졌을 가능성).");
			}
			else
			{
				_depth--;
			}
		}

		public static void RunSuppressed(Action action)
		{
			PushSuppressed();
			try
			{
				action();
			}
			finally
			{
				PopSuppressed();
			}
		}
	}
	[HarmonyPatch]
	public static class PPREquipMotionCancelPatch
	{
		private enum EquipClass
		{
			Shield,
			Helmet,
			Chest,
			Legs,
			Cape,
			Utility,
			Trinket,
			Weapon,
			Tool,
			Other
		}

		[HarmonyPatch(typeof(Player), "ToggleEquipped")]
		[HarmonyPrefix]
		public static bool ToggleEquipped_Prefix(Player __instance, ItemData item, ref bool __result)
		{
			if (!PPRConfig.EquipMotionCancelEnabled.Value)
			{
				return true;
			}
			if (item == null)
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			var (flag, flag2) = PPRExceptionGuard.Run("EquipMotionCancel.ToggleEquipped", () => ((bool, bool))TryInstantToggle(__instance, item), (false, false));
			if (!flag)
			{
				return true;
			}
			__result = flag2;
			return false;
		}

		private static (bool handled, bool result) TryInstantToggle(Player __instance, ItemData item)
		{
			if (!item.IsEquipable())
			{
				return (handled: false, result: false);
			}
			if (((Character)__instance).InAttack())
			{
				return (handled: true, result: true);
			}
			if (!IsCancelEnabledFor(item))
			{
				return (handled: false, result: false);
			}
			if (__instance.IsEquipActionQueued(item))
			{
				((Humanoid)__instance).RemoveEquipAction(item);
			}
			bool flag = ((Humanoid)__instance).IsItemEquiped(item);
			if (flag)
			{
				((Humanoid)__instance).UnequipItem(item, true);
			}
			else
			{
				((Humanoid)__instance).EquipItem(item, true);
			}
			PPRDebugLog.Log("PPR-EQUIP", string.Format("즉시 {0} — {1}({2})", flag ? "해제" : "장착", item.m_shared.m_name, ClassifyEquip(item)));
			return (handled: true, result: true);
		}

		private static EquipClass ClassifyEquip(ItemData item)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected I4, but got Unknown
			ItemType itemType = item.m_shared.m_itemType;
			ItemType val = itemType;
			switch (val - 3)
			{
			case 2:
				return EquipClass.Shield;
			case 3:
				return EquipClass.Helmet;
			case 4:
				return EquipClass.Chest;
			case 8:
				return EquipClass.Legs;
			case 14:
				return EquipClass.Cape;
			case 15:
				return EquipClass.Utility;
			case 21:
				return EquipClass.Trinket;
			case 16:
				return EquipClass.Tool;
			case 0:
			case 1:
			case 11:
			case 12:
			case 19:
				return EquipClass.Weapon;
			default:
				return EquipClass.Other;
			}
		}

		private static bool IsCancelEnabledFor(ItemData item)
		{
			EquipClass equipClass = ClassifyEquip(item);
			if (1 == 0)
			{
			}
			bool result = equipClass switch
			{
				EquipClass.Shield => PPRConfig.EquipCancelShield.Value, 
				EquipClass.Helmet => PPRConfig.EquipCancelHelmet.Value, 
				EquipClass.Chest => PPRConfig.EquipCancelChest.Value, 
				EquipClass.Legs => PPRConfig.EquipCancelLegs.Value, 
				EquipClass.Cape => PPRConfig.EquipCancelCape.Value, 
				EquipClass.Utility => PPRConfig.EquipCancelUtility.Value, 
				EquipClass.Trinket => PPRConfig.EquipCancelTrinket.Value, 
				EquipClass.Weapon => PPRConfig.EquipCancelWeapon.Value, 
				EquipClass.Tool => PPRConfig.EquipCancelTool.Value, 
				_ => false, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		[HarmonyPatch(typeof(Humanoid), "HideHandItems")]
		[HarmonyPrefix]
		public static void HideHandItems_Prefix(Humanoid __instance, ref bool animation)
		{
			if (PPRConfig.EquipMotionCancelEnabled.Value && PPRConfig.EquipCancelHideShow.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				if (__instance.LeftItem == null && __instance.RightItem == null)
				{
					animation = false;
				}
				else if (PPRExceptionGuard.Run("EquipMotionCancel.HideHandItems", LogHideCancelled, fallback: false))
				{
					animation = false;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "ShowHandItems")]
		[HarmonyPrefix]
		public static void ShowHandItems_Prefix(Humanoid __instance, ref bool animation)
		{
			if (PPRConfig.EquipMotionCancelEnabled.Value && PPRConfig.EquipCancelHideShow.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				if (PPRAccess.GetHiddenLeftItem(__instance) == null && PPRAccess.GetHiddenRightItem(__instance) == null)
				{
					animation = false;
				}
				else if (PPRExceptionGuard.Run("EquipMotionCancel.ShowHandItems", LogShowCancelled, fallback: false))
				{
					animation = false;
				}
			}
		}

		private static bool LogHideCancelled()
		{
			PPRDebugLog.Log("PPR-EQUIP", "손 아이템 숨김 — 모션 생략.");
			return true;
		}

		private static bool LogShowCancelled()
		{
			PPRDebugLog.Log("PPR-EQUIP", "손 아이템 꺼냄 — 모션 생략.");
			return true;
		}
	}
	internal static class PPRExceptionGuard
	{
		private const int SampleInterval = 200;

		private static readonly HashSet<string> _disabled = new HashSet<string>();

		private static readonly Dictionary<string, int> _skipCounts = new Dictionary<string, int>();

		public static void Run(string featureKey, Action logic)
		{
			if (IsDisabledAndCounted(featureKey))
			{
				return;
			}
			try
			{
				logic();
			}
			catch (Exception ex)
			{
				Disable(featureKey, ex);
			}
		}

		public static T Run<T>(string featureKey, Func<T> logic, T fallback)
		{
			if (IsDisabledAndCounted(featureKey))
			{
				return fallback;
			}
			try
			{
				return logic();
			}
			catch (Exception ex)
			{
				Disable(featureKey, ex);
				return fallback;
			}
		}

		private static bool IsDisabledAndCounted(string featureKey)
		{
			if (!_disabled.Contains(featureKey))
			{
				return false;
			}
			int value;
			int num = ((!_skipCounts.TryGetValue(featureKey, out value)) ? 1 : (value + 1));
			_skipCounts[featureKey] = num;
			if (num % 200 == 0)
			{
				Plugin.Log.LogWarning((object)$"[PPR:예외격리] '{featureKey}' 비활성화 상태 유지 중 — 누적 스킵 {num}회.");
			}
			return true;
		}

		private static void Disable(string featureKey, Exception ex)
		{
			_disabled.Add(featureKey);
			Plugin.Log.LogError((object)$"[PPR:예외격리] '{featureKey}' 예외 발생 — 이번 세션 동안 이 기능만 비활성화(나머지 PPR 기능/게임은 계속 동작):\n{ex}");
		}
	}
	internal static class PPRFreezeReporter
	{
		private static readonly object Lock = new object();

		private static string _reportPath;

		private static string ReportPath
		{
			get
			{
				if (_reportPath == null)
				{
					string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".";
					_reportPath = Path.Combine(path, "PPR-freeze-report.txt");
				}
				return _reportPath;
			}
		}

		public static void Append(string section, string body)
		{
			lock (Lock)
			{
				try
				{
					string contents = string.Format("===== [{0}] {1:yyyy-MM-dd HH:mm:ss} (PPR v{2}) =====\n{3}\n\n", section, DateTime.Now, "1.2.11", body);
					File.AppendAllText(ReportPath, contents);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("[PPR-REPORT] 진단 파일 기록 실패(무시): " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch]
	public static class PPRGuardDurabilityPatch
	{
		public enum BlockerClass
		{
			Shield,
			Tool,
			Weapon
		}

		private static bool _frontGuardPending;

		private static ItemData _pendingBlocker;

		private static float _pendingBlockerDurabilityBefore;

		private static float _pendingBlockableDamageBefore;

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		[HarmonyPrefix]
		public static void RPC_Damage_Prefix()
		{
			PPRGuardDurabilityState.Clear();
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		[HarmonyFinalizer]
		public static Exception RPC_Damage_Finalizer(Exception __exception)
		{
			PPRGuardDurabilityState.Clear();
			return __exception;
		}

		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		[HarmonyPrefix]
		public static void BlockAttack_Prefix(Humanoid __instance, HitData hit)
		{
			_frontGuardPending = false;
			if (hit != null && AnyProtectionEnabled())
			{
				PPRExceptionGuard.Run("GuardDurability.BlockAttack.Prefix", delegate
				{
					CaptureBeforeState(__instance, hit);
				});
			}
		}

		private static void CaptureBeforeState(Humanoid __instance, HitData hit)
		{
			ItemData currentBlocker = PPRAccess.GetCurrentBlocker(__instance);
			if (currentBlocker != null)
			{
				_pendingBlocker = currentBlocker;
				_pendingBlockerDurabilityBefore = currentBlocker.m_durability;
				_pendingBlockableDamageBefore = hit.GetTotalBlockableDamage();
				_frontGuardPending = true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		[HarmonyPostfix]
		public static void BlockAttack_Postfix(HitData hit, bool __result)
		{
			bool frontGuardPending = _frontGuardPending;
			_frontGuardPending = false;
			if (frontGuardPending && __result && hit != null)
			{
				PPRExceptionGuard.Run("GuardDurability.BlockAttack.Postfix", delegate
				{
					EvaluateAfterState(hit);
				});
			}
		}

		private static void EvaluateAfterState(HitData hit)
		{
			if (!(hit.GetTotalBlockableDamage() >= _pendingBlockableDamageBefore))
			{
				PPRGuardDurabilityState.MarkGuardSucceeded();
				ItemData pendingBlocker = _pendingBlocker;
				if (pendingBlocker != null && ShouldProtectBlocker(pendingBlocker) && pendingBlocker.m_durability < _pendingBlockerDurabilityBefore)
				{
					pendingBlocker.m_durability = _pendingBlockerDurabilityBefore;
					PPRDebugLog.Log("PPR-GUARD", $"블로커 내구도 복원(정면) — {pendingBlocker.m_shared.m_name}({ClassifyBlocker(pendingBlocker)})");
				}
			}
		}

		[HarmonyPatch(typeof(Player), "DamageArmorDurability")]
		[HarmonyPrefix]
		public static bool DamageArmorDurability_Prefix(Player __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (!PPRConfig.NoArmorWearOnGuard.Value)
			{
				return true;
			}
			return PPRExceptionGuard.Run("GuardDurability.DamageArmorDurability", ShouldSkipArmorDurability, fallback: true);
		}

		private static bool ShouldSkipArmorDurability()
		{
			if (!PPRGuardDurabilityState.GuardSucceeded)
			{
				return true;
			}
			PPRDebugLog.Log("PPR-GUARD", "방어구 내구도 감소 생략 (가드 성공)");
			return false;
		}

		public static BlockerClass ClassifyBlocker(ItemData item)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			if ((int)item.m_shared.m_itemType == 5)
			{
				return BlockerClass.Shield;
			}
			if ((int)item.m_shared.m_itemType == 19 || (int)item.m_shared.m_skillType == 12)
			{
				return BlockerClass.Tool;
			}
			return BlockerClass.Weapon;
		}

		public static bool ShouldProtectBlocker(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			BlockerClass blockerClass = ClassifyBlocker(item);
			if (1 == 0)
			{
			}
			bool result = blockerClass switch
			{
				BlockerClass.Shield => PPRConfig.NoDurabilityLossShield.Value, 
				BlockerClass.Tool => PPRConfig.NoDurabilityLossTool.Value, 
				_ => PPRConfig.NoDurabilityLossWeapon.Value, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static bool AnyProtectionEnabled()
		{
			return PPRConfig.NoArmorWearOnGuard.Value || PPRConfig.NoDurabilityLossWeapon.Value || PPRConfig.NoDurabilityLossTool.Value || PPRConfig.NoDurabilityLossShield.Value;
		}
	}
	internal static class PPRGuardDurabilityState
	{
		private static bool _guardSucceeded;

		public static bool GuardSucceeded => _guardSucceeded;

		public static void MarkGuardSucceeded()
		{
			_guardSucceeded = true;
		}

		public static void Clear()
		{
			_guardSucceeded = false;
		}
	}
	[HarmonyPatch]
	public static class PPRGuardSprintPatch
	{
		private const float LogDebounceSeconds = 0.2f;

		private const float MinSegmentLogSeconds = 0.5f;

		private static bool _dumped;

		private static bool _diagGuardWalkLogged;

		private static bool _diagGuardSprintLogged;

		private static bool _diagPlainSprintLogged;

		private static readonly int ParamForwardSpeed = Animator.StringToHash("forward_speed");

		private static readonly int ParamSidewaySpeed = Animator.StringToHash("sideway_speed");

		private static readonly int ParamTurnSpeed = Animator.StringToHash("turn_speed");

		private static readonly int ParamBlocking = Animator.StringToHash("blocking");

		private static readonly int ParamOnGround = Animator.StringToHash("onground");

		[HarmonyPatch(typeof(Humanoid), "CheckRun")]
		[HarmonyPrefix]
		public static void CheckRun_Prefix(Humanoid __instance)
		{
			PPRGuardSprintState.SuppressBlockCheck = false;
			Player player = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			PPRExceptionGuard.Run("GuardSprint.CheckRun", delegate
			{
				if (TryGetEligibleBlocker(player, out var _))
				{
					PPRGuardSprintState.SuppressBlockCheck = true;
				}
			});
		}

		[HarmonyPatch(typeof(Humanoid), "CheckRun")]
		[HarmonyFinalizer]
		public static Exception CheckRun_Finalizer(Exception __exception)
		{
			PPRGuardSprintState.SuppressBlockCheck = false;
			return __exception;
		}

		[HarmonyPatch(typeof(Humanoid), "IsBlocking")]
		[HarmonyPrefix]
		public static bool IsBlocking_SuppressPrefix(ref bool __result)
		{
			if (!PPRGuardSprintState.SuppressBlockCheck)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(Player), "GetRunSpeedFactor")]
		[HarmonyPostfix]
		public static void GetRunSpeedFactor_Postfix(Player __instance, ref float __result)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				float current = __result;
				__result = PPRExceptionGuard.Run("GuardSprint.Speed", () => ApplySpeed(__instance, current), current);
			}
		}

		private static float ApplySpeed(Player player, float baseFactor)
		{
			ItemData blocker;
			bool flag = TryGetEligibleBlocker(player, out blocker);
			PPRShieldClass cls = (flag ? PPRShieldClassifier.ClassifyBlockerForSpeed(blocker) : PPRShieldClass.Buckler);
			float num = (flag ? PPRShieldClassifier.GetPenalty(cls) : 0f);
			bool rawActive = flag && ((Character)player).IsBlocking();
			bool flag2 = UpdateActiveDebounce(player, rawActive);
			float num2 = (flag2 ? (1f - num) : 1f);
			float speedMps = ((Character)player).m_runSpeed * baseFactor * num2;
			UpdateSegmentTracking(player, flag2, blocker, cls, num, speedMps);
			return baseFactor * num2;
		}

		private static bool UpdateActiveDebounce(Player player, bool rawActive)
		{
			if (rawActive)
			{
				PPRGuardSprintState.SetInactiveGraceTimer(player, 0f);
			}
			else
			{
				PPRGuardSprintState.SetInactiveGraceTimer(player, PPRGuardSprintState.InactiveGraceTimer(player) + Time.deltaTime);
			}
			return rawActive || PPRGuardSprintState.InactiveGraceTimer(player) < 0.2f;
		}

		private static void UpdateSegmentTracking(Player player, bool effectiveActive, ItemData blocker, PPRShieldClass cls, float penalty, float speedMps)
		{
			PPRSprintSegment pPRSprintSegment = (effectiveActive ? PPRSprintSegment.Guard : PPRSprintSegment.Plain);
			PPRSprintSegment pPRSprintSegment2 = PPRGuardSprintState.CurrentSegment(player);
			if (pPRSprintSegment != pPRSprintSegment2)
			{
				LogSegmentEnd(player, pPRSprintSegment2);
				PPRGuardSprintState.ResetSegment(player, pPRSprintSegment);
				if (pPRSprintSegment == PPRSprintSegment.Guard)
				{
					string text = blocker?.m_shared?.m_name ?? "?";
					PPRGuardSprintState.SetLastShield(player, text, cls);
					if (PPRConfig.VerboseLog.Value)
					{
						float value = PPRConfig.GuardSprintStaminaMultiplier.Value;
						PPRDebugLog.Log("PPR-SPRINT", $"가드 질주 시작 — {text}({cls}), 감속 {penalty * 100f:F0}%, {speedMps:F2}m/s, 스태미나 배율 {value:F2}");
					}
				}
			}
			var (actual, vanilla) = PPRGuardSprintState.ConsumePendingStamina(player);
			PPRGuardSprintState.AddSegmentStamina(player, actual, vanilla);
			PPRGuardSprintState.AdvanceSegmentElapsed(player, Time.deltaTime);
			PPRGuardSprintState.SetSegmentLastSpeedMps(player, speedMps);
		}

		private static void CloseCurrentSegment(Player player)
		{
			PPRSprintSegment pPRSprintSegment = PPRGuardSprintState.CurrentSegment(player);
			if (pPRSprintSegment != PPRSprintSegment.None)
			{
				LogSegmentEnd(player, pPRSprintSegment);
				PPRGuardSprintState.ResetSegment(player, PPRSprintSegment.None);
			}
		}

		private static void LogSegmentEnd(Player player, PPRSprintSegment segment)
		{
			if (segment == PPRSprintSegment.None || !PPRConfig.VerboseLog.Value)
			{
				return;
			}
			float num = PPRGuardSprintState.SegmentElapsed(player);
			if (!(num < 0.5f))
			{
				float num2 = PPRGuardSprintState.SegmentActualStamina(player);
				float num3 = PPRGuardSprintState.SegmentLastSpeedMps(player);
				float num4 = ((num > 0.0001f) ? (num2 / num) : 0f);
				if (segment == PPRSprintSegment.Guard)
				{
					float num5 = PPRGuardSprintState.SegmentVanillaStamina(player);
					float num6 = ((num5 > 0.0001f) ? (num2 / num5) : 1f);
					string text = PPRGuardSprintState.LastShieldName(player);
					PPRShieldClass pPRShieldClass = PPRGuardSprintState.LastShieldClass(player);
					PPRDebugLog.Log("PPR-SPRINT", $"가드 질주 종료 — {text}({pPRShieldClass}), 지속 {num:F1}s, 스태미나 {num2:F1}/{num5:F1}(비율 {num6:F2}), 초당 {num4:F2}");
				}
				else
				{
					PPRDebugLog.Log("PPR-SPRINT", $"그냥 질주 종료 — 지속 {num:F1}s, {num3:F2}m/s, 스태미나 {num2:F1}, 초당 {num4:F2}");
				}
			}
		}

		[HarmonyPatch(typeof(Player), "CheckRun")]
		[HarmonyPrefix]
		public static void PlayerCheckRun_Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				PPRGuardSprintState.SetInsideCheckRunStaminaScope(__instance, value: true);
			}
		}

		[HarmonyPatch(typeof(Player), "CheckRun")]
		[HarmonyPostfix]
		public static void PlayerCheckRun_Postfix(Player __instance, ref bool __result)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !__result)
			{
				PPRExceptionGuard.Run("GuardSprint.SegmentClose", delegate
				{
					CloseCurrentSegment(__instance);
				});
			}
		}

		[HarmonyPatch(typeof(Player), "CheckRun")]
		[HarmonyFinalizer]
		public static Exception PlayerCheckRun_Finalizer(Player __instance, Exception __exception)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				PPRGuardSprintState.SetInsideCheckRunStaminaScope(__instance, value: false);
			}
			return __exception;
		}

		[HarmonyPatch(typeof(Player), "UseStamina")]
		[HarmonyPrefix]
		public static void UseStamina_Prefix(Player __instance, ref float v)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && PPRGuardSprintState.InsideCheckRunStaminaScope(__instance))
			{
				float before = v;
				v = PPRExceptionGuard.Run("GuardSprint.StaminaMultiply", () => TallyAndMultiplyRunStamina(__instance, before), before);
			}
		}

		private static float TallyAndMultiplyRunStamina(Player player, float before)
		{
			float num = before;
			if (TryGetEligibleBlocker(player, out var _) && ((Character)player).IsBlocking())
			{
				num *= Mathf.Max(0f, PPRConfig.GuardSprintStaminaMultiplier.Value);
			}
			PPRGuardSprintState.AddPendingStamina(player, num, before);
			return num;
		}

		public static bool TryGetEligibleBlocker(Player player, out ItemData blocker)
		{
			blocker = null;
			if (!PPRConfig.GuardSprintEnabled.Value)
			{
				return false;
			}
			ItemData currentBlocker = PPRAccess.GetCurrentBlocker((Humanoid)(object)player);
			if (currentBlocker == null)
			{
				return false;
			}
			float value = PPRConfig.GuardSprintMinBlockingSkill.Value;
			if (value > 0f)
			{
				float skillLevel = ((Character)player).GetSkillLevel((SkillType)6);
				if (skillLevel < value)
				{
					if (((Character)player).IsBlocking() && !PPRGuardSprintState.SkillTooLowLatched(player))
					{
						PPRDebugLog.Log("PPR-SPRINT", $"가드 질주 불가 — 막기 스킬 부족({skillLevel:F0} < {value:F0})");
						PPRGuardSprintState.SetSkillTooLowLatched(player, value: true);
					}
					return false;
				}
				PPRGuardSprintState.SetSkillTooLowLatched(player, value: false);
			}
			blocker = currentBlocker;
			return true;
		}

		public static float GetJogSpeedMps(Player player)
		{
			return ((Character)player).m_speed * (1f + ((Character)player).GetEquipmentMovementModifier());
		}

		public static float GetHorizontalSpeedMps(Player player)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Vector3 velocity = ((Character)player).GetVelocity();
			velocity.y = 0f;
			return ((Vector3)(ref velocity)).magnitude;
		}

		public static bool TryGetLegSyncMultiplier(Player player, out float multiplier)
		{
			multiplier = 1f;
			if (!PPRConfig.GuardSprintSyncLegAnimation.Value)
			{
				return false;
			}
			if (!TryGetEligibleBlocker(player, out var _))
			{
				return false;
			}
			if (!((Character)player).IsBlocking() || !((Character)player).IsRunning())
			{
				return false;
			}
			if (((Character)player).InDodge() || ((Character)player).IsStaggering() || ((Character)player).InAttack())
			{
				return false;
			}
			float jogSpeedMps = GetJogSpeedMps(player);
			if (jogSpeedMps <= 0.0001f)
			{
				return false;
			}
			float num = GetHorizontalSpeedMps(player) / jogSpeedMps;
			float num2 = Mathf.Max(1f, PPRConfig.GuardSprintMaxLegSpeedUp.Value);
			multiplier = Mathf.Clamp(num, 1f, num2);
			return true;
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		[HarmonyPostfix]
		public static void ObjectDB_CopyOtherDB_Postfix(ObjectDB __instance)
		{
			if (!_dumped && PPRConfig.VerboseLog.Value)
			{
				PPRExceptionGuard.Run("GuardSprint.ShieldDump", delegate
				{
					DumpShields(__instance);
				});
			}
		}

		private static void DumpShields(ObjectDB db)
		{
			List<ItemDrop> allItems = db.GetAllItems((ItemType)5, "");
			if (allItems == null || allItems.Count == 0)
			{
				return;
			}
			_dumped = true;
			Plugin.Log.LogInfo((object)$"[PPR-SPRINT] 방패 판별 덤프 — {allItems.Count}개(바닐라+모드 전체, ObjectDB.CopyOtherDB 이후)");
			foreach (ItemDrop item in allItems)
			{
				SharedData shared = item.m_itemData.m_shared;
				PPRShieldClass pPRShieldClass = PPRShieldClassifier.Classify(((Object)((Component)item).gameObject).name, shared);
				Plugin.Log.LogInfo((object)$"[PPR-SPRINT]  {((Object)((Component)item).gameObject).name} / {shared.m_name} -> {pPRShieldClass} (timedBlockBonus={shared.m_timedBlockBonus:F2}, movementModifier={shared.m_movementModifier:F2})");
			}
		}

		public static void TickAnimatorDiagnostics(Player player)
		{
			if (PPRConfig.VerboseLog.Value)
			{
				PPRExceptionGuard.Run("GuardSprint.AnimDiag", delegate
				{
					DiagnoseAnimator(player);
				});
			}
		}

		private static void DiagnoseAnimator(Player player)
		{
			ItemData blocker;
			bool flag = TryGetEligibleBlocker(player, out blocker);
			bool flag2 = ((Character)player).IsBlocking();
			bool flag3 = ((Character)player).IsRunning();
			bool flag4 = GetHorizontalSpeedMps(player) > 0.1f;
			if (!_diagGuardWalkLogged && flag && flag2 && !flag3 && flag4)
			{
				_diagGuardWalkLogged = true;
				DumpAnimatorState(player, "가드하고 걷기");
			}
			else if (!_diagGuardSprintLogged && flag && flag2 && flag3)
			{
				_diagGuardSprintLogged = true;
				DumpAnimatorState(player, "가드 질주");
			}
			else if (!_diagPlainSprintLogged && flag3 && !flag2)
			{
				_diagPlainSprintLogged = true;
				DumpAnimatorState(player, "그냥 질주");
			}
		}

		private static void DumpAnimatorState(Player player, string situation)
		{
			Animator componentInChildren = ((Component)player).GetComponentInChildren<Animator>();
			if ((Object)(object)componentInChildren == (Object)null)
			{
				Plugin.Log.LogInfo((object)("[PPR-SPRINT-ANIM] " + situation + " — Animator를 찾지 못함."));
				return;
			}
			Plugin.Log.LogInfo((object)$"[PPR-SPRINT-ANIM] === {situation} — 레이어 {componentInChildren.layerCount}개 ===");
			for (int i = 0; i < componentInChildren.layerCount; i++)
			{
				string layerName = componentInChildren.GetLayerName(i);
				float num = ((i == 0) ? 1f : componentInChildren.GetLayerWeight(i));
				AnimatorClipInfo[] currentAnimatorClipInfo = componentInChildren.GetCurrentAnimatorClipInfo(i);
				string text = ((currentAnimatorClipInfo.Length == 0) ? "(클립 없음)" : string.Join(", ", Array.ConvertAll(currentAnimatorClipInfo, delegate(AnimatorClipInfo c)
				{
					AnimationClip clip = ((AnimatorClipInfo)(ref c)).clip;
					return $"{((clip != null) ? ((Object)clip).name : null)}({((AnimatorClipInfo)(ref c)).weight:F2})";
				})));
				Plugin.Log.LogInfo((object)$"[PPR-SPRINT-ANIM]  레이어[{i}] {layerName} weight={num:F2} 클립={text}");
			}
			Plugin.Log.LogInfo((object)($"[PPR-SPRINT-ANIM]  파라미터 forward_speed={componentInChildren.GetFloat(ParamForwardSpeed):F2} " + $"sideway_speed={componentInChildren.GetFloat(ParamSidewaySpeed):F2} turn_speed={componentInChildren.GetFloat(ParamTurnSpeed):F2} " + $"blocking={componentInChildren.GetBool(ParamBlocking)} onground={componentInChildren.GetBool(ParamOnGround)}"));
		}
	}
	internal enum PPRSprintSegment
	{
		None,
		Guard,
		Plain
	}
	internal static class PPRGuardSprintState
	{
		private class Entry
		{
			public float InactiveGraceTimer = 999f;

			public bool SkillTooLowLatched;

			public string LastShieldName = "?";

			public PPRShieldClass LastShieldClass;

			public PPRSprintSegment CurrentSegment = PPRSprintSegment.None;

			public float SegmentElapsed;

			public float SegmentActualStamina;

			public float SegmentVanillaStamina;

			public float SegmentLastSpeedMps;

			public float PendingActualStamina;

			public float PendingVanillaStamina;

			public bool InsideCheckRunStaminaScope;
		}

		private static readonly Dictionary<Player, Entry> _entries = new Dictionary<Player, Entry>();

		public static bool SuppressBlockCheck;

		private static Entry Get(Player player)
		{
			if (!_entries.TryGetValue(player, out var value))
			{
				value = new Entry();
				_entries[player] = value;
			}
			return value;
		}

		public static float InactiveGraceTimer(Player player)
		{
			return Get(player).InactiveGraceTimer;
		}

		public static void SetInactiveGraceTimer(Player player, float value)
		{
			Get(player).InactiveGraceTimer = value;
		}

		public static bool SkillTooLowLatched(Player player)
		{
			return Get(player).SkillTooLowLatched;
		}

		public static void SetSkillTooLowLatched(Player player, bool value)
		{
			Get(player).SkillTooLowLatched = value;
		}

		public static string LastShieldName(Player player)
		{
			return Get(player).LastShieldName;
		}

		public static PPRShieldClass LastShieldClass(Player player)
		{
			return Get(player).LastShieldClass;
		}

		public static void SetLastShield(Player player, string name, PPRShieldClass cls)
		{
			Entry entry = Get(player);
			entry.LastShieldName = name;
			entry.LastShieldClass = cls;
		}

		public static PPRSprintSegment CurrentSegment(Player player)
		{
			return Get(player).CurrentSegment;
		}

		public static float SegmentElapsed(Player player)
		{
			return Get(player).SegmentElapsed;
		}

		public static float SegmentActualStamina(Player player)
		{
			return Get(player).SegmentActualStamina;
		}

		public static float SegmentVanillaStamina(Player player)
		{
			return Get(player).SegmentVanillaStamina;
		}

		public static float SegmentLastSpeedMps(Player player)
		{
			return Get(player).SegmentLastSpeedMps;
		}

		public static void ResetSegment(Player player, PPRSprintSegment segment)
		{
			Entry entry = Get(player);
			entry.CurrentSegment = segment;
			entry.SegmentElapsed = 0f;
			entry.SegmentActualStamina = 0f;
			entry.SegmentVanillaStamina = 0f;
		}

		public static void AdvanceSegmentElapsed(Player player, float dt)
		{
			Get(player).SegmentElapsed += dt;
		}

		public static void AddSegmentStamina(Player player, float actual, float vanilla)
		{
			Entry entry = Get(player);
			entry.SegmentActualStamina += actual;
			entry.SegmentVanillaStamina += vanilla;
		}

		public static void SetSegmentLastSpeedMps(Player player, float value)
		{
			Get(player).SegmentLastSpeedMps = value;
		}

		public static void AddPendingStamina(Player player, float actual, float vanilla)
		{
			Entry entry = Get(player);
			entry.PendingActualStamina += actual;
			entry.PendingVanillaStamina += vanilla;
		}

		public static (float actual, float vanilla) ConsumePendingStamina(Player player)
		{
			Entry entry = Get(player);
			float pendingActualStamina = entry.PendingActualStamina;
			float pendingVanillaStamina = entry.PendingVanillaStamina;
			entry.PendingActualStamina = 0f;
			entry.PendingVanillaStamina = 0f;
			return (actual: pendingActualStamina, vanilla: pendingVanillaStamina);
		}

		public static bool InsideCheckRunStaminaScope(Player player)
		{
			return Get(player).InsideCheckRunStaminaScope;
		}

		public static void SetInsideCheckRunStaminaScope(Player player, bool value)
		{
			Get(player).InsideCheckRunStaminaScope = value;
		}
	}
	[HarmonyPatch]
	public static class PPRProjectileReflectPatch
	{
		private static Projectile _currentProjectile = null;

		private static Aoe _currentAoe = null;

		private static DamageTypes _originalDamage;

		private static readonly FieldInfo _fVel = AccessTools.Field(typeof(Projectile), "m_vel");

		private static readonly FieldInfo _fOwner = AccessTools.Field(typeof(Projectile), "m_owner");

		private static readonly FieldInfo _fOrigHit = AccessTools.Field(typeof(Projectile), "m_originalHitData");

		private static readonly FieldInfo _fDamage = AccessTools.Field(typeof(Projectile), "m_damage");

		private static readonly FieldInfo _fBlock = AccessTools.Field(typeof(Projectile), "m_blockable");

		private static readonly FieldInfo _fDodge = AccessTools.Field(typeof(Projectile), "m_dodgeable");

		private static readonly FieldInfo _fForce = AccessTools.Field(typeof(Projectile), "m_attackForce");

		private static readonly FieldInfo _fBackstab = AccessTools.Field(typeof(Projectile), "m_backstabBonus");

		private static readonly FieldInfo _fHealth = AccessTools.Field(typeof(Projectile), "m_healthReturn");

		private static readonly FieldInfo _fSEHash = AccessTools.Field(typeof(Projectile), "m_statusEffectHash");

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Projectile), "OnHit")]
		public static void OnHit_Prefix(Projectile __instance)
		{
			_currentProjectile = __instance;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Projectile), "OnHit")]
		public static void OnHit_Postfix()
		{
			_currentProjectile = null;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Aoe), "OnHit", new Type[]
		{
			typeof(Collider),
			typeof(Vector3)
		})]
		public static bool AoeOnHit_Prefix(Aoe __instance)
		{
			_currentAoe = __instance;
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Aoe), "OnHit", new Type[]
		{
			typeof(Collider),
			typeof(Vector3)
		})]
		public static void AoeOnHit_Postfix()
		{
			_currentAoe = null;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		public static void BlockAttack_Prefix(HitData hit)
		{
			if (!((Object)(object)_currentProjectile == (Object)null) || !((Object)(object)_currentAoe == (Object)null))
			{
				PPRExceptionGuard.Run("ProjectileReflect.SaveOriginalDamage", delegate
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0011: Unknown result type (might be due to invalid IL or missing references)
					_originalDamage = ((DamageTypes)(ref hit.m_damage)).Clone();
				});
			}
		}

		public static void TryReflect(Humanoid blocker, HitData hit, Character attacker, bool isPerfect)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			Component val = (Component)(((object)_currentProjectile) ?? ((object)_currentAoe));
			if ((Object)(object)val == (Object)null || (Object)(object)attacker == (Object)null)
			{
				return;
			}
			try
			{
				string text = ((Object)val.gameObject).name.Replace("(Clone)", "").Trim();
				ZNetScene instance = ZNetScene.instance;
				GameObject val2 = ((instance != null) ? instance.GetPrefab(text) : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogWarning((object)("[PPR] 투사체 반사: prefab 없음 (" + text + ")"));
					return;
				}
				float num = 10f;
				if ((Object)(object)_currentProjectile != (Object)null && _fVel != null)
				{
					Vector3 val3 = (Vector3)_fVel.GetValue(_currentProjectile);
					num = Mathf.Max(((Vector3)(ref val3)).magnitude, 10f);
				}
				else if ((Object)(object)_currentAoe != (Object)null)
				{
					Rigidbody component = ((Component)_currentAoe).GetComponent<Rigidbody>();
					if ((Object)(object)component != (Object)null)
					{
						Vector3 linearVelocity = component.linearVelocity;
						num = Mathf.Max(((Vector3)(ref linearVelocity)).magnitude, 10f);
					}
				}
				Vector3 val4 = ((Character)blocker).GetLookDir();
				if (((Vector3)(ref val4)).sqrMagnitude < 0.001f)
				{
					val4 = ((Component)blocker).transform.forward;
				}
				Vector3 val5 = ((Component)blocker).transform.position + Vector3.up * 1.5f;
				GameObject val6 = Object.Instantiate<GameObject>(val2, val5, Quaternion.LookRotation(val4));
				float num2 = (isPerfect ? PPRConfig.ProjectileReflectDamageMult.Value : PPRConfig.NormalParryReflectDamageMult.Value);
				HitData val7 = new HitData();
				val7.m_damage = ((DamageTypes)(ref _originalDamage)).Clone();
				((DamageTypes)(ref val7.m_damage)).Modify(num2);
				val7.m_pushForce = hit.m_pushForce;
				val7.m_toolTier = hit.m_toolTier;
				val7.m_ranged = true;
				val7.SetAttacker((Character)(object)blocker);
				Projectile component2 = val6.GetComponent<Projectile>();
				if ((Object)(object)component2 != (Object)null)
				{
					SetupProjectileReflection(component2, (Character)(object)blocker, val4 * num, val7);
				}
				else
				{
					IProjectile val8 = val6.GetComponent<IProjectile>() ?? val6.GetComponentInChildren<IProjectile>();
					if (val8 == null)
					{
						ZNetScene instance2 = ZNetScene.instance;
						if (instance2 != null)
						{
							instance2.Destroy(val6);
						}
						return;
					}
					val8.Setup((Character)(object)blocker, val4 * num, -1f, val7, (ItemData)null, (ItemData)null);
				}
				string text2 = (isPerfect ? "퍼펙트" : "일반");
				Plugin.Log.LogInfo((object)("[PPR] " + text2 + " 투사체 반사: " + text + " " + $"(speed={num:F1}, dmg={((DamageTypes)(ref val7.m_damage)).GetTotalDamage():F1}x{num2})"));
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[PPR] 투사체 반사 오류: {arg}");
			}
		}

		private static void SetupProjectileReflection(Projectile proj, Character owner, Vector3 vel, HitData hitData)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			_fOwner?.SetValue(proj, owner);
			_fVel?.SetValue(proj, vel);
			_fOrigHit?.SetValue(proj, hitData);
			_fDamage?.SetValue(proj, hitData.m_damage);
			_fBlock?.SetValue(proj, hitData.m_blockable);
			_fDodge?.SetValue(proj, hitData.m_dodgeable);
			_fForce?.SetValue(proj, hitData.m_pushForce);
			_fBackstab?.SetValue(proj, hitData.m_backstabBonus);
			_fHealth?.SetValue(proj, hitData.m_healthReturn);
			_fSEHash?.SetValue(proj, hitData.m_statusEffectHash);
		}
	}
	[HarmonyPatch]
	public static class PPRRangedNoStaggerPatch
	{
		private static Character _patchedAttacker;

		private static bool _savedStaggerWhenBlocked;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		public static void BlockAttack_Prefix(HitData hit, Character attacker)
		{
			_patchedAttacker = null;
			if (PPRConfig.RangedNoStagger.Value && !((Object)(object)attacker == (Object)null))
			{
				PPRExceptionGuard.Run("RangedNoStagger.BlockAttack.Prefix", delegate
				{
					PrefixLogic(hit, attacker);
				});
			}
		}

		private static void PrefixLogic(HitData hit, Character attacker)
		{
			bool flag = hit?.m_ranged ?? false;
			bool flag2 = IsRangedWeaponEquipped(attacker);
			if (flag || flag2)
			{
				_patchedAttacker = attacker;
				_savedStaggerWhenBlocked = attacker.m_staggerWhenBlocked;
				attacker.m_staggerWhenBlocked = false;
				Plugin.Log.LogInfo((object)$"[PPR] 원거리 BlockAttack: m_staggerWhenBlocked=false (rangedHit={flag}, rangedWeapon={flag2}, attacker={((Object)attacker).name})");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		public static void BlockAttack_Postfix()
		{
			if (!((Object)(object)_patchedAttacker == (Object)null))
			{
				PPRExceptionGuard.Run("RangedNoStagger.BlockAttack.Postfix", delegate
				{
					_patchedAttacker.m_staggerWhenBlocked = _savedStaggerWhenBlocked;
					_patchedAttacker = null;
				});
			}
		}

		private static bool IsRangedWeaponEquipped(Character c)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected I4, but got Unknown
			try
			{
				if ((Object)(object)c == (Object)null)
				{
					return false;
				}
				Humanoid val = (Humanoid)(object)((c is Humanoid) ? c : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				ItemData currentWeapon = val.GetCurrentWeapon();
				if (currentWeapon == null)
				{
					return false;
				}
				int num = (int)currentWeapon.m_shared.m_itemType;
				return num == 5 || num == 19;
			}
			catch
			{
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "RemoveObjects")]
	public static class PPRRemoveObjectsDiagnosticsPatch
	{
		private const int DetailedLogLimit = 5;

		private const int SampledLogInterval = 100;

		private static int _exceptionCount = 0;

		private static readonly HashSet<string> BossaddKnownVfx = new HashSet<string>(StringComparer.Ordinal) { "lightningAOE", "vfx_GodExplosion", "fx_eikthyr_stomp", "vfx_spawn_small" };

		[HarmonyFinalizer]
		public static Exception Finalizer(Exception __exception)
		{
			if (__exception == null)
			{
				return null;
			}
			int num = ++_exceptionCount;
			if (num <= 5)
			{
				string arg = LogGhostEntries(num);
				Plugin.Log.LogError((object)$"[RO-DIAG] RemoveObjects 예외 #{num}:\n{__exception}");
				PPRFreezeReporter.Append("RemoveObjects 예외", $"발생 횟수: #{num}\n\n예외:\n{__exception}\n\n유령 ZNetView 스캔 결과:\n{arg}");
			}
			else if (num % 100 == 0)
			{
				Plugin.Log.LogError((object)$"[RO-DIAG] RemoveObjects 예외 누적 {num}회 ({100}회마다 샘플 로그)");
			}
			return __exception;
		}

		private static string LogGhostEntries(int occurrence)
		{
			try
			{
				ZNetScene instance = ZNetScene.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return "(ZNetScene.instance == null)";
				}
				S