BepInEx\plugins\KorCaptain-PPR\PPR.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[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;

[BepInPlugin("com.KorCaptain.PPR", "PPR", "1.2.1")]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGUID = "com.KorCaptain.PPR";

	public const string PluginName = "PPR";

	public const string PluginVersion = "1.2.1";

	internal static ManualLogSource Log;

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

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		PPRConfig.Init(((BaseUnityPlugin)this).Config);
		_harmony.PatchAll();
		Log.LogInfo((object)"PPR v1.2.1 loaded.");
	}
}
internal static class PPRAccess
{
	public static ItemData GetCurrentWeapon(Humanoid h)
	{
		return Traverse.Create((object)h).Method("GetCurrentWeapon", Array.Empty<object>()).GetValue<ItemData>();
	}

	public static ItemData GetCurrentBlocker(Humanoid h)
	{
		return Traverse.Create((object)h).Method("GetCurrentBlocker", Array.Empty<object>()).GetValue<ItemData>();
	}

	public static bool HaveSecondaryAttack(ItemData item)
	{
		return Traverse.Create((object)item).Method("HaveSecondaryAttack", Array.Empty<object>()).GetValue<bool>();
	}

	public static bool HaveStamina(Character c, float amount)
	{
		return Traverse.Create((object)c).Method("HaveStamina", new Type[1] { typeof(float) }, new object[1] { amount }).GetValue<bool>();
	}

	public static bool IsStaggering(Character c)
	{
		return Traverse.Create((object)c).Method("IsStaggering", Array.Empty<object>()).GetValue<bool>();
	}

	public static bool InAttack(Character c)
	{
		return Traverse.Create((object)c).Method("InAttack", Array.Empty<object>()).GetValue<bool>();
	}

	public static bool CanMove(Character c)
	{
		return Traverse.Create((object)c).Method("CanMove", Array.Empty<object>()).GetValue<bool>();
	}
}
[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
public static class PPRBlockPatch
{
	[HarmonyPostfix]
	public static void Postfix(Humanoid __instance, HitData hit, Character attacker, bool __result, float ___m_blockTimer)
	{
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Invalid comparison between Unknown and I4
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Invalid comparison between Unknown and I4
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Invalid comparison between Unknown and I4
		try
		{
			if (!__result)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			bool flag = ___m_blockTimer >= 0f;
			ItemData currentBlocker = PPRAccess.GetCurrentBlocker(__instance);
			bool flag2 = currentBlocker != null && currentBlocker.m_shared.m_timedBlockBonus > 1f;
			if (PPRConfig.Enable.Value && flag && ___m_blockTimer < PPRConfig.ParryWindow.Value && flag2 && PPRAccess.HaveStamina((Character)(object)__instance, 0f) && !PPRAccess.IsStaggering((Character)(object)__instance))
			{
				ItemData currentWeapon = PPRAccess.GetCurrentWeapon(__instance);
				if (currentWeapon != null)
				{
					ItemType itemType = currentWeapon.m_shared.m_itemType;
					if (((int)itemType == 3 || (int)itemType == 14 || (int)itemType == 22) && PPRAccess.HaveSecondaryAttack(currentWeapon))
					{
						PPRState.ActivateParry(val);
						PPRState.SpawnParryVFX(val);
					}
				}
				Plugin.Log.LogInfo((object)$"[PPR] 퍼펙트 패링 (blockTimer={___m_blockTimer:F3}s)");
				if (hit.m_ranged && PPRConfig.ProjectileReflect.Value)
				{
					PPRProjectileReflectPatch.TryReflect(__instance, hit, attacker, isPerfect: true);
				}
			}
			else if (flag && hit.m_ranged && PPRConfig.NormalParryReflect.Value)
			{
				Plugin.Log.LogInfo((object)$"[PPR] 일반 패링 (blockTimer={___m_blockTimer:F3}s)");
				PPRProjectileReflectPatch.TryReflect(__instance, hit, attacker, isPerfect: false);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[PPR] BlockPatch 오류: " + ex.Message));
		}
	}
}
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";

	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 void Init(ConfigFile cfg)
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Expected O, but got Unknown
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Expected O, but got Unknown
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Expected O, but got Unknown
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Expected O, but got Unknown
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Expected O, but got Unknown
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Expected O, but got Unknown
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Expected O, but got Unknown
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Expected O, but got Unknown
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Expected O, but got Unknown
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01da: Expected O, but got Unknown
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Expected O, but got Unknown
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0217: Expected O, but got Unknown
		//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bf: Expected O, but got Unknown
		//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c4: Expected O, but got Unknown
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		//IL_0317: Expected O, but got Unknown
		//IL_0312: Unknown result type (might be due to invalid IL or missing references)
		//IL_031c: Expected O, but got Unknown
		Enable = cfg.Bind<bool>("Perfect Parry", "(Enable) Perfect Parry", true, "Enable or disable the Perfect Parry feature.");
		ParryWindow = cfg.Bind<float>("Perfect Parry", "(Parry) Detection Window (sec)", 0.1f, new ConfigDescription("Time window to register a perfect parry. Must be shorter than the base perfect block window (0.25s).", (AcceptableValueBase)new AcceptableValueRange<float>(0.01f, 0.24f), Array.Empty<object>()));
		CounterWindow = cfg.Bind<float>("Perfect Parry", "(Counter) Input Window (sec)", 1f, new ConfigDescription("Time after a successful parry during which you can press the secondary attack key to trigger a counter.", (AcceptableValueBase)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
		CounterSpeedMultiplier = cfg.Bind<float>("Perfect Parry", "(Counter) Animation Speed Multiplier", 2f, new ConfigDescription("Animation speed multiplier for the counter special attack. 2.5 = 2.5x faster.", (AcceptableValueBase)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
		DamageReductionEnabled = cfg.Bind<bool>("Perfect Parry - Damage Reduction", "(Enable) Damage Reduction", true, "Reduce incoming damage during the counter window after a successful parry.");
		DamageReductionPercent = cfg.Bind<float>("Perfect Parry - Damage Reduction", "(Damage Reduction) Reduction Rate (%)", 50f, new ConfigDescription("Damage reduction percentage during the counter window. 50 = 50% less damage.", (AcceptableValueBase)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		VFXEnabled = cfg.Bind<bool>("Perfect Parry - Effect", "(Enable) Head Effect", true, "Show a VFX above the player's head on successful parry.");
		VFXPrefab = cfg.Bind<string>("Perfect Parry - Effect", "(Effect) VFX Name", "vfx_blocked", "Name of the VFX prefab to display. Must be a valid ZNetScene prefab name.");
		VFXOffsetX = cfg.Bind<float>("Perfect Parry - Effect", "(Effect) Offset X", 0f, new ConfigDescription("Horizontal offset (left/right) relative to the player.", (AcceptableValueBase)new AcceptableValueRange<float>(-5f, 5f), Array.Empty<object>()));
		VFXOffsetY = cfg.Bind<float>("Perfect Parry - Effect", "(Effect) Offset Y", 1.25f, new ConfigDescription("Vertical offset (height) relative to the player. Default 2.5 = above head.", (AcceptableValueBase)new AcceptableValueRange<float>(-2f, 8f), Array.Empty<object>()));
		VFXOffsetZ = cfg.Bind<float>("Perfect Parry - Effect", "(Effect) Offset Z", 0f, new ConfigDescription("Depth offset (forward/back) relative to the player.", (AcceptableValueBase)new AcceptableValueRange<float>(-5f, 5f), Array.Empty<object>()));
		VFXColorEnabled = cfg.Bind<bool>("Perfect Parry - Effect", "(Effect) Custom Color Enabled", true, "Apply a custom color to the perfect parry VFX. Only affects the PPR-spawned effect, not the vanilla block VFX.");
		VFXColorHex = cfg.Bind<string>("Perfect Parry - Effect", "(Effect) Custom Color (Hex)", "#4DB8FF", "Hex color code for the perfect parry VFX. Examples: #FF4444 (red), #44FF88 (green), #4DB8FF (blue), #FFD700 (gold).");
		RangedNoStagger = cfg.Bind<bool>("Perfect Parry - Ranged", "(Enable) Ranged No Stagger", true, "Ranged attackers are not staggered when their projectile is blocked or parried.");
		ProjectileReflect = cfg.Bind<bool>("Perfect Parry - Ranged", "(Enable) Projectile Reflect", true, "Reflect projectiles back on perfect parry.");
		ProjectileReflectDamageMult = cfg.Bind<float>("Perfect Parry - Ranged", "(Reflect) Perfect Parry Damage Multiplier", 3f, new ConfigDescription("Damage multiplier for reflected projectiles on perfect parry.", (AcceptableValueBase)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
		NormalParryReflect = cfg.Bind<bool>("Perfect Parry - Ranged", "(Enable) Normal Parry Reflect", true, "Reflect projectiles on normal parry (vanilla timed block window, outside perfect parry window).");
		NormalParryReflectDamageMult = cfg.Bind<float>("Perfect Parry - Ranged", "(Reflect) Normal Parry Damage Multiplier", 2f, new ConfigDescription("Damage multiplier for reflected projectiles on normal parry.", (AcceptableValueBase)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
	}
}
[HarmonyPatch(typeof(Character), "ApplyDamage")]
public static class PPRDamagePatch
{
	[HarmonyPrefix]
	public static void Prefix(Character __instance, HitData hit)
	{
		try
		{
			if (PPRConfig.Enable.Value && PPRConfig.DamageReductionEnabled.Value)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && PPRState.IsParryWindowActive(val) && hit != null)
				{
					float num = 1f - PPRConfig.DamageReductionPercent.Value / 100f;
					((DamageTypes)(ref hit.m_damage)).Modify(num);
					Plugin.Log.LogInfo((object)$"[PPR] 데미지 감소 적용 ({PPRConfig.DamageReductionPercent.Value}%)");
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[PPR] DamagePatch 오류: " + ex.Message));
		}
	}
}
[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)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_currentProjectile != (Object)null || (Object)(object)_currentAoe != (Object)null)
		{
			_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_025c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0260: 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)
				{
					Object.Destroy((Object)(object)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))
		{
			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)
		{
			_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(Game), "Awake")]
public static class PPRSpeedRegistrar
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static Handler <>9__1_0;

		internal double <Postfix>b__1_0(Character character, double speed)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			try
			{
				if (!PPRConfig.Enable.Value)
				{
					return speed;
				}
				Player val = (Player)((character is Player) ? character : null);
				if ((Object)(object)val == (Object)null)
				{
					return speed;
				}
				if (!PPRState.IsCounterSpeedActive(val))
				{
					return speed;
				}
				if (!PPRAccess.InAttack((Character)val))
				{
					return speed;
				}
				return speed * (double)PPRConfig.CounterSpeedMultiplier.Value;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[PPR] SpeedCallback 오류: " + ex.Message));
				return speed;
			}
		}
	}

	private static bool _registered;

	[HarmonyPostfix]
	public static void Postfix()
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Expected O, but got Unknown
		if (_registered)
		{
			return;
		}
		_registered = true;
		object obj = <>c.<>9__1_0;
		if (obj == null)
		{
			Handler val = delegate(Character character, double speed)
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Expected O, but got Unknown
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Expected O, but got Unknown
				try
				{
					if (!PPRConfig.Enable.Value)
					{
						return speed;
					}
					Player val2 = (Player)((character is Player) ? character : null);
					if ((Object)(object)val2 == (Object)null)
					{
						return speed;
					}
					if (!PPRState.IsCounterSpeedActive(val2))
					{
						return speed;
					}
					if (!PPRAccess.InAttack((Character)val2))
					{
						return speed;
					}
					return speed * (double)PPRConfig.CounterSpeedMultiplier.Value;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("[PPR] SpeedCallback 오류: " + ex.Message));
					return speed;
				}
			};
			<>c.<>9__1_0 = val;
			obj = (object)val;
		}
		AnimationSpeedManager.Add((Handler)obj, 400);
		Plugin.Log.LogInfo((object)"[PPR] AnimationSpeedManager 콜백 등록 완료.");
	}
}
[HarmonyPatch(typeof(Humanoid), "StartAttack")]
public static class PPRStartAttackPatch
{
	private static readonly FieldInfo _timeSinceLastAttackField = AccessTools.Field(typeof(Humanoid), "m_timeSinceLastAttack");

	private static readonly FieldInfo _currentAttackIsSecondaryField = AccessTools.Field(typeof(Humanoid), "m_currentAttackIsSecondary");

	private static bool _counterHandled = false;

	private static float _savedTimeSinceLastAttack = 0f;

	[HarmonyPrefix]
	public static void Prefix(Humanoid __instance, bool secondaryAttack)
	{
		_counterHandled = false;
		try
		{
			if (!PPRConfig.Enable.Value || !secondaryAttack)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && PPRState.IsParryWindowActive(val))
			{
				ItemData currentWeapon = PPRAccess.GetCurrentWeapon(__instance);
				if (currentWeapon == null || !PPRAccess.HaveSecondaryAttack(currentWeapon))
				{
					Plugin.Log.LogWarning((object)"[PPR] 카운터 실패 - 현재 무기에 특수 공격 없음");
					PPRState.ConsumeParry(val);
					return;
				}
				_savedTimeSinceLastAttack = ((_timeSinceLastAttackField != null) ? ((float)_timeSinceLastAttackField.GetValue(__instance)) : 0f);
				_counterHandled = true;
				PPRState.ConsumeParry(val);
				PPRState.ActivateCounterSpeed(val);
				Plugin.Log.LogInfo((object)$"[PPR] 카운터 발동! InAttack={PPRAccess.InAttack((Character)(object)__instance)}, CanMove={PPRAccess.CanMove((Character)(object)__instance)}");
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[PPR] StartAttackPatch 오류: " + ex.Message));
		}
	}

	[HarmonyPostfix]
	public static void Postfix(Humanoid __instance, bool secondaryAttack)
	{
		if (!_counterHandled)
		{
			return;
		}
		_counterHandled = false;
		try
		{
			_timeSinceLastAttackField?.SetValue(__instance, _savedTimeSinceLastAttack);
			_currentAttackIsSecondaryField?.SetValue(__instance, false);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[PPR] StartAttackPatch Postfix 오류: " + ex.Message));
		}
	}
}
public static class PPRState
{
	private static readonly Dictionary<Player, float> _counterWindow = new Dictionary<Player, float>();

	private static readonly HashSet<Player> _speedActive = new HashSet<Player>();

	private static readonly Dictionary<Player, bool> _prevInAttack = new Dictionary<Player, bool>();

	private static readonly Dictionary<Player, GameObject> _parryVFX = new Dictionary<Player, GameObject>();

	public static void ActivateParry(Player player)
	{
		_counterWindow[player] = PPRConfig.CounterWindow.Value;
		Plugin.Log.LogInfo((object)"[PPR] 퍼펙트 패링 성공!");
	}

	public static bool IsParryWindowActive(Player player)
	{
		float value;
		return _counterWindow.TryGetValue(player, out value) && value > 0f;
	}

	public static void ConsumeParry(Player player)
	{
		_counterWindow[player] = 0f;
		DestroyParryVFX(player);
	}

	public static void Tick(Player player, float dt)
	{
		if (_counterWindow.TryGetValue(player, out var value) && !(value <= 0f))
		{
			float num = Mathf.Max(0f, value - dt);
			_counterWindow[player] = num;
			if (num <= 0f)
			{
				DestroyParryVFX(player);
			}
		}
	}

	public static void ActivateCounterSpeed(Player player)
	{
		_speedActive.Add(player);
		_prevInAttack[player] = false;
	}

	public static bool IsCounterSpeedActive(Player player)
	{
		return _speedActive.Contains(player);
	}

	public static void TickCounterSpeed(Player player, bool isInAttack)
	{
		bool value;
		if (!_speedActive.Contains(player))
		{
			_prevInAttack.Remove(player);
		}
		else if (_prevInAttack.TryGetValue(player, out value) && value && !isInAttack)
		{
			DeactivateCounterSpeed(player);
		}
		else
		{
			_prevInAttack[player] = isInAttack;
		}
	}

	public static void DeactivateCounterSpeed(Player player)
	{
		_speedActive.Remove(player);
		_prevInAttack.Remove(player);
		Plugin.Log.LogInfo((object)"[PPR] 카운터 속도 해제");
	}

	public static void SpawnParryVFX(Player player)
	{
		//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_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		DestroyParryVFX(player);
		if (!PPRConfig.VFXEnabled.Value)
		{
			return;
		}
		string value = PPRConfig.VFXPrefab.Value;
		if (string.IsNullOrEmpty(value) || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		GameObject prefab = ZNetScene.instance.GetPrefab(value);
		if ((Object)(object)prefab == (Object)null)
		{
			Plugin.Log.LogWarning((object)("[PPR] VFX 프리팹 없음: " + value));
			return;
		}
		Vector3 val = default(Vector3);
		((Vector3)(ref val))..ctor(PPRConfig.VFXOffsetX.Value, PPRConfig.VFXOffsetY.Value, PPRConfig.VFXOffsetZ.Value);
		prefab.SetActive(false);
		GameObject val2 = Object.Instantiate<GameObject>(prefab, ((Component)player).transform.position + val, Quaternion.identity);
		prefab.SetActive(true);
		val2.transform.SetParent(((Component)player).transform, false);
		val2.transform.localPosition = val;
		ZNetView[] componentsInChildren = val2.GetComponentsInChildren<ZNetView>(true);
		foreach (ZNetView val3 in componentsInChildren)
		{
			Object.Destroy((Object)(object)val3);
		}
		ZSyncTransform[] componentsInChildren2 = val2.GetComponentsInChildren<ZSyncTransform>(true);
		foreach (ZSyncTransform val4 in componentsInChildren2)
		{
			Object.Destroy((Object)(object)val4);
		}
		ApplyVFXColor(val2);
		val2.SetActive(true);
		_parryVFX[player] = val2;
		Plugin.Log.LogInfo((object)("[PPR] VFX 스폰: " + value));
	}

	private static void ApplyVFXColor(GameObject vfxObj)
	{
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: 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_0115: Expected O, but got Unknown
		//IL_0241: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: 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)
		//IL_01a5: 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_029e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: 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_02e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_020b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0327: Unknown result type (might be due to invalid IL or missing references)
		//IL_032d: Unknown result type (might be due to invalid IL or missing references)
		if (!PPRConfig.VFXColorEnabled.Value)
		{
			return;
		}
		string text = PPRConfig.VFXColorHex.Value?.Trim() ?? "#4DB8FF";
		if (!text.StartsWith("#"))
		{
			text = "#" + text;
		}
		Color val = default(Color);
		if (!ColorUtility.TryParseHtmlString(text, ref val))
		{
			Plugin.Log.LogWarning((object)("[PPR] VFX 색상 파싱 실패: " + text));
			return;
		}
		ParticleSystem[] componentsInChildren = vfxObj.GetComponentsInChildren<ParticleSystem>(true);
		foreach (ParticleSystem val2 in componentsInChildren)
		{
			MainModule main = val2.main;
			MinMaxGradient val3 = ((MainModule)(ref main)).startColor;
			Color color = ((MinMaxGradient)(ref val3)).color;
			((MainModule)(ref main)).startColor = new MinMaxGradient(new Color(val.r, val.g, val.b, color.a));
			ColorOverLifetimeModule colorOverLifetime = val2.colorOverLifetime;
			if (!((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled)
			{
				continue;
			}
			val3 = ((ColorOverLifetimeModule)(ref colorOverLifetime)).color;
			Gradient gradient = ((MinMaxGradient)(ref val3)).gradient;
			Gradient val4 = new Gradient();
			GradientColorKey[] array = (GradientColorKey[])(object)new GradientColorKey[(gradient != null) ? gradient.colorKeys.Length : 2];
			if (gradient != null && gradient.colorKeys.Length != 0)
			{
				for (int j = 0; j < gradient.colorKeys.Length; j++)
				{
					array[j] = new GradientColorKey(val, gradient.colorKeys[j].time);
				}
			}
			else
			{
				array[0] = new GradientColorKey(val, 0f);
				array[1] = new GradientColorKey(val, 1f);
			}
			GradientAlphaKey[] array2 = (GradientAlphaKey[])((gradient != null && gradient.alphaKeys.Length != 0) ? ((Array)gradient.alphaKeys) : ((Array)new GradientAlphaKey[2]
			{
				new GradientAlphaKey(1f, 0f),
				new GradientAlphaKey(0f, 1f)
			}));
			val4.SetKeys(array, array2);
			((ColorOverLifetimeModule)(ref colorOverLifetime)).color = new MinMaxGradient(val4);
		}
		Light[] componentsInChildren2 = vfxObj.GetComponentsInChildren<Light>(true);
		foreach (Light val5 in componentsInChildren2)
		{
			val5.color = val;
		}
		Renderer[] componentsInChildren3 = vfxObj.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val6 in componentsInChildren3)
		{
			Material[] materials = val6.materials;
			foreach (Material val7 in materials)
			{
				if (val7.HasProperty("_Color"))
				{
					val7.color = new Color(val.r, val.g, val.b, val7.color.a);
				}
				if (val7.HasProperty("_TintColor"))
				{
					val7.SetColor("_TintColor", new Color(val.r, val.g, val.b, val7.GetColor("_TintColor").a));
				}
				if (val7.HasProperty("_EmissionColor"))
				{
					val7.SetColor("_EmissionColor", val * 2f);
				}
			}
		}
		Plugin.Log.LogInfo((object)("[PPR] VFX 색상 적용: " + text));
	}

	public static void DestroyParryVFX(Player player)
	{
		if (_parryVFX.TryGetValue(player, out var value) && (Object)(object)value != (Object)null)
		{
			Object.Destroy((Object)(object)value);
		}
		_parryVFX.Remove(player);
	}
}
[HarmonyPatch(typeof(Player), "Update")]
public static class PPRTickPatch
{
	[HarmonyPostfix]
	public static void Postfix(Player __instance)
	{
		try
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				PPRState.Tick(__instance, Time.deltaTime);
				PPRState.TickCounterSpeed(__instance, PPRAccess.InAttack((Character)(object)__instance));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[PPR] TickPatch 오류: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
public static class PPRVFXSuppressPatch
{
	private static EffectList _targetList;

	private static EffectData[] _savedEffects;

	[HarmonyPrefix]
	public static void Prefix(Humanoid __instance, float ___m_blockTimer)
	{
		_targetList = null;
		_savedEffects = null;
		if (!PPRConfig.Enable.Value || !PPRConfig.VFXEnabled.Value || !PPRConfig.VFXColorEnabled.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))
		{
			ItemData currentBlocker = PPRAccess.GetCurrentBlocker(__instance);
			if (currentBlocker != null && !(currentBlocker.m_shared.m_timedBlockBonus <= 1f) && ___m_blockTimer >= 0f && ___m_blockTimer < PPRConfig.ParryWindow.Value)
			{
				_targetList = currentBlocker.m_shared.m_blockEffect;
				_savedEffects = _targetList.m_effectPrefabs;
				_targetList.m_effectPrefabs = Array.Empty<EffectData>();
			}
		}
	}

	[HarmonyPostfix]
	public static void Postfix()
	{
		if (_targetList != null && _savedEffects != null)
		{
			_targetList.m_effectPrefabs = _savedEffects;
			_targetList = null;
			_savedEffects = null;
		}
	}
}