// WL850_SwordArc.cs — 검 1타 궤적을 가로 베기로 덮어쓰고(보정 행) 검 직업 호 크기를 칼끝 반경에 맞춘다(직업 오버라이드) · 에디터 · 멱등 // PD 2026-10-12 #850 「검 1타 공격의 궤적을 상단이 아니라 횡으로 · 검 이펙트 크기가 검 크기에 비해 너무 크다」 // 값 = AgentScripts/WL850_SwordArc.json (C45) // unity command run_script --file AgentScripts/WL850_SwordArc.cs --entry WL850_SwordArc.Apply --timeout 120 // 🔴 WL828_TrailBake 로 10101·10102 1타를 다시 구우면 행이 측정값으로 돌아간다 → 그 뒤 이 도구를 다시 돌린다. using System.Collections.Generic; using System.Text; using UnityEditor; using UnityEngine; using WL.Combat; public static class WL850_SwordArc { const string SpecPath = "AgentScripts/WL850_SwordArc.json"; const string SettingsPath = "Assets/WL/Settings/Resources/WL/SlashTrailSettings.asset"; class Spec { public List rows; public List classOverrides; } class RowSpec { public int classId; public string clipName; public float[] planeNormal, bisector, pivot; public float sweepDeg; public float tipRadius = -1f; } class OvSpec { public int classId; public bool arcFitBlade; public float arcSizeMul = 1f; } static Vector3 V(float[] a) { return new Vector3(a[0], a[1], a[2]); } public static string Apply() { var spec = Newtonsoft.Json.JsonConvert.DeserializeObject(System.IO.File.ReadAllText(SpecPath)); var st = AssetDatabase.LoadAssetAtPath(SettingsPath); if (st == null) return "settings 없음"; var sb = new StringBuilder(); foreach (var r in spec.rows) { var cal = st.FindCalibration(r.classId, r.clipName); if (cal == null) { sb.Append("행 없음 ").Append(r.classId).Append(' ').Append(r.clipName).Append('\n'); continue; } Vector3 n = V(r.planeNormal).normalized, b = V(r.bisector); b -= n * Vector3.Dot(b, n); b.Normalize(); // 시작 방향 = 이등분을 법선 축으로 −sweep/2 돌린 것(시작→이등분이 법선 기준 양의 회전 = WL828 규약) Vector3 start = Quaternion.AngleAxis(-r.sweepDeg * 0.5f, n) * b; string before = "n=" + cal.planeNormal.ToString("F2") + " bis=" + cal.bisector.ToString("F2") + " pivot=" + cal.pivot.ToString("F2") + " sweep=" + cal.sweepDeg.ToString("F0") + " tipR=" + cal.tipRadius.ToString("F2"); cal.kind = SwingKind.Swing; cal.planeNormal = n; cal.bisector = b; cal.startDir = start.normalized; cal.pivot = V(r.pivot); cal.sweepDeg = r.sweepDeg; if (r.tipRadius > 0f) cal.tipRadius = r.tipRadius; cal.source = "manual #850 가로 베기 " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm") + " (WL850_SwordArc · 이전 " + before + ")"; sb.Append(r.classId).Append(' ').Append(r.clipName).Append(" ← 가로 n=").Append(n.ToString("F2")).Append(" start=").Append(cal.startDir.ToString("F2")).Append(" pivot=").Append(cal.pivot.ToString("F2")).Append(" sweep=").Append(r.sweepDeg).Append(" tipR=").Append(cal.tipRadius.ToString("F2")).Append('\n'); } // 직업 오버라이드(#850 새 필드 — 컴파일 전이면 건너뛴다) var so = new SerializedObject(st); var arr = so.FindProperty("classOverrides"); foreach (var o in spec.classOverrides) { bool found = false; for (int i = 0; arr != null && i < arr.arraySize; i++) { var el = arr.GetArrayElementAtIndex(i); if (el.FindPropertyRelative("classId").intValue != o.classId) continue; found = true; var pf = el.FindPropertyRelative("arcFitBlade"); var pm = el.FindPropertyRelative("arcSizeMul"); if (pf == null || pm == null) { sb.Append("직업 ").Append(o.classId).Append(" 오버라이드 필드 없음(컴파일 전)\n"); break; } pf.boolValue = o.arcFitBlade; pm.floatValue = o.arcSizeMul; sb.Append("직업 ").Append(o.classId).Append(" arcFitBlade=").Append(o.arcFitBlade).Append(" arcSizeMul=").Append(o.arcSizeMul).Append('\n'); } if (!found) sb.Append("직업 ").Append(o.classId).Append(" 오버라이드 행 없음\n"); } so.ApplyModifiedPropertiesWithoutUndo(); EditorUtility.SetDirty(st); AssetDatabase.SaveAssets(); return sb.ToString(); } }