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()).GetValue(); } public static ItemData GetCurrentBlocker(Humanoid h) { return Traverse.Create((object)h).Method("GetCurrentBlocker", Array.Empty()).GetValue(); } public static bool HaveSecondaryAttack(ItemData item) { return Traverse.Create((object)item).Method("HaveSecondaryAttack", Array.Empty()).GetValue(); } 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(); } public static bool IsStaggering(Character c) { return Traverse.Create((object)c).Method("IsStaggering", Array.Empty()).GetValue(); } public static bool InAttack(Character c) { return Traverse.Create((object)c).Method("InAttack", Array.Empty()).GetValue(); } public static bool CanMove(Character c) { return Traverse.Create((object)c).Method("CanMove", Array.Empty()).GetValue(); } } [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 Enable; public static ConfigEntry ParryWindow; public static ConfigEntry CounterWindow; public static ConfigEntry CounterSpeedMultiplier; public static ConfigEntry DamageReductionEnabled; public static ConfigEntry DamageReductionPercent; public static ConfigEntry VFXEnabled; public static ConfigEntry VFXPrefab; public static ConfigEntry VFXOffsetX; public static ConfigEntry VFXOffsetY; public static ConfigEntry VFXOffsetZ; public static ConfigEntry VFXColorEnabled; public static ConfigEntry VFXColorHex; public static ConfigEntry RangedNoStagger; public static ConfigEntry ProjectileReflect; public static ConfigEntry ProjectileReflectDamageMult; public static ConfigEntry NormalParryReflect; public static ConfigEntry 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("Perfect Parry", "(Enable) Perfect Parry", true, "Enable or disable the Perfect Parry feature."); ParryWindow = cfg.Bind("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(0.01f, 0.24f), Array.Empty())); CounterWindow = cfg.Bind("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(0.1f, 2f), Array.Empty())); CounterSpeedMultiplier = cfg.Bind("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(1f, 5f), Array.Empty())); DamageReductionEnabled = cfg.Bind("Perfect Parry - Damage Reduction", "(Enable) Damage Reduction", true, "Reduce incoming damage during the counter window after a successful parry."); DamageReductionPercent = cfg.Bind("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(0f, 100f), Array.Empty())); VFXEnabled = cfg.Bind("Perfect Parry - Effect", "(Enable) Head Effect", true, "Show a VFX above the player's head on successful parry."); VFXPrefab = cfg.Bind("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("Perfect Parry - Effect", "(Effect) Offset X", 0f, new ConfigDescription("Horizontal offset (left/right) relative to the player.", (AcceptableValueBase)new AcceptableValueRange(-5f, 5f), Array.Empty())); VFXOffsetY = cfg.Bind("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(-2f, 8f), Array.Empty())); VFXOffsetZ = cfg.Bind("Perfect Parry - Effect", "(Effect) Offset Z", 0f, new ConfigDescription("Depth offset (forward/back) relative to the player.", (AcceptableValueBase)new AcceptableValueRange(-5f, 5f), Array.Empty())); VFXColorEnabled = cfg.Bind("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("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("Perfect Parry - Ranged", "(Enable) Ranged No Stagger", true, "Ranged attackers are not staggered when their projectile is blocked or parried."); ProjectileReflect = cfg.Bind("Perfect Parry - Ranged", "(Enable) Projectile Reflect", true, "Reflect projectiles back on perfect parry."); ProjectileReflectDamageMult = cfg.Bind("Perfect Parry - Ranged", "(Reflect) Perfect Parry Damage Multiplier", 3f, new ConfigDescription("Damage multiplier for reflected projectiles on perfect parry.", (AcceptableValueBase)new AcceptableValueRange(0f, 10f), Array.Empty())); NormalParryReflect = cfg.Bind("Perfect Parry - Ranged", "(Enable) Normal Parry Reflect", true, "Reflect projectiles on normal parry (vanilla timed block window, outside perfect parry window)."); NormalParryReflectDamageMult = cfg.Bind("Perfect Parry - Ranged", "(Reflect) Normal Parry Damage Multiplier", 2f, new ConfigDescription("Damage multiplier for reflected projectiles on normal parry.", (AcceptableValueBase)new AcceptableValueRange(0f, 10f), Array.Empty())); } } [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(); 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(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(); if ((Object)(object)component2 != (Object)null) { SetupProjectileReflection(component2, (Character)(object)blocker, val4 * num, val7); } else { IProjectile val8 = val6.GetComponent() ?? val6.GetComponentInChildren(); 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 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 _counterWindow = new Dictionary(); private static readonly HashSet _speedActive = new HashSet(); private static readonly Dictionary _prevInAttack = new Dictionary(); private static readonly Dictionary _parryVFX = new Dictionary(); 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(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(true); foreach (ZNetView val3 in componentsInChildren) { Object.Destroy((Object)(object)val3); } ZSyncTransform[] componentsInChildren2 = val2.GetComponentsInChildren(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(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(true); foreach (Light val5 in componentsInChildren2) { val5.color = val; } Renderer[] componentsInChildren3 = vfxObj.GetComponentsInChildren(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(); } } } [HarmonyPostfix] public static void Postfix() { if (_targetList != null && _savedEffects != null) { _targetList.m_effectPrefabs = _savedEffects; _targetList = null; _savedEffects = null; } } }