Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PPR v1.2.1
BepInEx\plugins\KorCaptain-PPR\PPR.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using 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; } } }