// ───────────────────────────────────────────────────────────────────────────── // WLSkillHitFeel.cs — 스킬별 피격 반응 덮어쓰기(밀림 · 넉백 · 경직) (#915 · 2026-10-17) // // PD 원문: 「검 각성 공격에 적중 된 적은 경직 상태로 살짝만 밀리게 해줘(현재는 밀리는 거리 때문에 가장자리에 있는 적이 밀려나 맞지 않을 수 있음)」 // ■ 그 스킬(맞힌 투사체의 SkillListTableData.e_Skill)에 맞은 몹만: // · 피격 밀기(MobStagger #903 mobHitPushDistance) 거리 × pushScale // · 원본 넉백(능력치 AttackKnockBack → Actor.KnockBack) 거리 × knockbackScale(MobKnockbackScale 이 몹 넉백 배율 대신 쓴다) // · 경직 = max(등급별 경직, staggerSeconds) // ■ 값 = Resources/WL/WLSkillHitFeel.json (C45) · 끄기 = enabled 0 · RuntimeDisabled(프로브 전후 비교용) // 🔴 Debug.LogError 금지(원본 팝업). // ───────────────────────────────────────────────────────────────────────────── using System.Collections.Generic; using UnityEngine; using WL.Combat.Core; namespace WL.Combat.Reaction { [System.Serializable] public class WLSkillHitFeelRule { public string skill = ""; // eEffect 이름(원본 스킬 클래스) public float pushScale = 1f; // 피격 밀기 거리 배율 public float pushMeters; // #974 > 0 = 피격 밀기 거리 고정(m · pushScale 대신) — PD 「0.2m 정도 넉백」 public float knockbackScale = 1f; // 원본 넉백 거리 배율(몹 넉백 배율 대신) public float staggerSeconds; // 경직 최소 시간(0 = 등급별 그대로) } [System.Serializable] public class WLSkillHitFeelJson { public int enabled = 1; public WLSkillHitFeelRule[] rules = new WLSkillHitFeelRule[0]; public string note = ""; } public static class WLSkillHitFeel { public const string ResourcesPath = "WL/WLSkillHitFeel"; public static bool RuntimeDisabled; public static string LastLog = ""; static WLSkillHitFeelJson s_cfg; static readonly Dictionary s_rules = new Dictionary(); static WLSkillHitFeelJson Cfg { get { if (s_cfg != null) return s_cfg; s_cfg = new WLSkillHitFeelJson(); s_rules.Clear(); var ta = Resources.Load(ResourcesPath); if (ta == null) { LastLog = "설정 없음 " + ResourcesPath; return s_cfg; } try { JsonUtility.FromJsonOverwrite(ta.text, s_cfg); } catch (System.Exception ex) { LastLog = "설정 파싱 실패 — " + ex.Message; } if (s_cfg.rules != null) foreach (var r in s_cfg.rules) { eEffect ef; if (r != null && System.Enum.TryParse(r.skill, out ef)) s_rules[(int)ef]= r; else LastLog = "스킬 이름 없음 " + (r != null ? r.skill : "null"); } return s_cfg; } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void Reset() { s_cfg = null; s_rules.Clear(); RuntimeDisabled = false; } /// #918 원본 Actor 피해 처리의 넉백 훅 1줄 — 이 피해를 낸 스킬 규칙의 knockbackScale 이 0 이하면 원본 넉백을 아예 시작하지 않는다 /// (넉백 거리 배율은 시작된 뒤에 줄이므로 0 이어도 맞을 때마다 첫 프레임만큼(약 0.07 m) 밀렸다 · 연타 8 회면 약 0.5 m). public static bool SkipKnockback(DamageInfo d) { if (RuntimeDisabled || d == null) return false; var c = Cfg; if (c.enabled == 0 || s_rules.Count == 0) return false; var pd = d.Beater_pd; var s = pd != null ? pd.m_SkillListTableData : null; if (s == null) return false; WLSkillHitFeelRule r; if (!s_rules.TryGetValue((int)s.e_Skill, out r) || r.knockbackScale > 0f) return false; KnockbackSkipped++; return true; } public static int KnockbackSkipped; /// 이 피격을 낸 스킬의 규칙(없으면 null). public static WLSkillHitFeelRule For(in HitConfirmedEvent e) { if (RuntimeDisabled) return null; var c = Cfg; if (c.enabled == 0 || s_rules.Count == 0) return null; var pd = e.projectile; var s = pd != null ? pd.m_SkillListTableData : null; if (s == null) return null; WLSkillHitFeelRule r; return s_rules.TryGetValue((int)s.e_Skill, out r) ? r : null; } } }