// WL760_Verify.cs — PD 지시 #760 2-A 검증 전용 (읽기 전용 · Assets 무수정) // unity command run_script --file AgentScripts/WL760_Verify.cs --entry WL760_Verify.Assets // unity command run_script --file AgentScripts/WL760_Verify.cs --entry WL760_Verify.DumpEvents // unity command run_script --file AgentScripts/WL760_Verify.cs --entry WL760_Verify.DumpControllers // unity command run_script --file AgentScripts/WL760_Verify.cs --entry WL760_Verify.DumpMask using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEditor; public static class WL760_Verify { // ── 에셋 상태 (머티리얼·셰이더·설정) ───────────────────────────────────── public static object Assets() { var sb = new StringBuilder(); foreach (var p in new[] { "Assets/WL/Combat/Materials/M_WL_BladeTrail_Add.mat", "Assets/WL/Combat/Materials/M_WL_BladeTrail_Alpha.mat" }) { var m = AssetDatabase.LoadAssetAtPath(p); if (m == null) { sb.AppendLine("MAT MISSING " + p); continue; } sb.AppendLine("MAT " + m.name + " shader=" + (m.shader != null ? m.shader.name : "(null)") + " isSupported=" + (m.shader != null && m.shader.isSupported) + " passCount=" + (m.shader != null ? m.shader.passCount : -1)); foreach (var id in m.GetTexturePropertyNames()) { var t = m.GetTexture(id); if (t != null) sb.AppendLine(" tex " + id + " = " + t.name); } } var st = AssetDatabase.LoadAssetAtPath( "Assets/WL/Settings/Resources/WL/SlashTrailSettings.asset"); if (st == null) sb.AppendLine("SETTINGS MISSING"); else sb.AppendLine("SETTINGS enable=" + st.enableWeaponTrail + " uvU=" + st.uvUMin + "~" + st.uvUMax + " uvV=" + st.uvVMin + "~" + st.uvVMax + " fade=" + st.fadeSeconds + " writeCustom=" + st.writeCustomDataChannels + " suppressMode=" + st.suppressMode + " keep=[" + string.Join(",", st.keepEmitterNames ?? new string[0]) + "]" + " burstMode=" + st.burstMode + " matAdd=" + (st.materialAdditive != null ? st.materialAdditive.name : "(null)")); var map = AssetDatabase.LoadAssetAtPath("Assets/WL/Combat/CombatAnimMapping.asset"); if (map == null) sb.AppendLine("MAPPING MISSING"); else { sb.AppendLine("MAPPING classes=" + map.classes.Count + " backupRoot=" + map.backupRoot); foreach (var c in map.classes) sb.AppendLine(" " + c.classId + " states=" + c.states.Count + " ev=" + string.Join("/", c.states.Select(s => s.stateName + ":" + s.events.Count))); } return sb.ToString(); } // ── 컨트롤러 상태 → 모션 클립 실측 ─────────────────────────────────────── static readonly string[] Ctrls = { "Assets/Res_Addr/Animations/pcanim_onehand.controller", "Assets/Res_Addr/Animations/pcanim_spellblade.controller", "Assets/Res_Addr/Animations/pcanim_bluntshield.controller", "Assets/Res_Addr/Animations/pcanim_paladin.controller", "Assets/Res_Addr/Animations/pcanim_bladedancer.controller", }; public static object DumpControllers() { var sb = new StringBuilder(); foreach (var p in Ctrls) { var c = AssetDatabase.LoadAssetAtPath(p); if (c == null) { sb.AppendLine("CTRL MISSING " + p); continue; } sb.AppendLine("== " + System.IO.Path.GetFileName(p)); foreach (var layer in c.layers) foreach (var cs in layer.stateMachine.states) { var n = cs.state.name; if (n.IndexOf("ttack", StringComparison.OrdinalIgnoreCase) < 0) continue; var clip = cs.state.motion as AnimationClip; sb.AppendLine(" " + n + " -> " + (clip != null ? clip.name + " (" + clip.length.ToString("0.000") + "s, " + AssetDatabase.GetAssetPath(clip) + ")" : "(null)")); } } return sb.ToString(); } // ── 🔴 이벤트 전수 덤프 (판정 ⓑ) ───────────────────────────────────────── public static object DumpEvents() { var sb = new StringBuilder(); sb.AppendLine("class\tstate\tclip\tlen\tfunc\ttime\tnorm\tstr\ti\tf"); foreach (var p in Ctrls) { var c = AssetDatabase.LoadAssetAtPath(p); if (c == null) continue; string cn = System.IO.Path.GetFileNameWithoutExtension(p).Replace("pcanim_", ""); foreach (var layer in c.layers) foreach (var cs in layer.stateMachine.states) { var n = cs.state.name; if (n.IndexOf("ttack", StringComparison.OrdinalIgnoreCase) < 0) continue; var clip = cs.state.motion as AnimationClip; if (clip == null) { sb.AppendLine(cn + "\t" + n + "\t(null)"); continue; } var evs = AnimationUtility.GetAnimationEvents(clip); if (evs.Length == 0) sb.AppendLine(cn + "\t" + n + "\t" + clip.name + "\t" + clip.length.ToString("0.000") + "\t(no events)"); foreach (var e in evs) sb.AppendLine(cn + "\t" + n + "\t" + clip.name + "\t" + clip.length.ToString("0.000") + "\t" + e.functionName + "\t" + e.time.ToString("0.00000") + "\t" + (clip.length > 0 ? (e.time / clip.length).ToString("0.000") : "-") + "\t" + (string.IsNullOrEmpty(e.stringParameter) ? "-" : e.stringParameter) + "\t" + e.intParameter + "\t" + e.floatParameter); } } return sb.ToString(); } // ── 런타임 이미터 억제 상태 (판정 ⓓ) ───────────────────────────────────── public static object DumpMask() { var m = WL.Combat.WeaponTrailDriver.LastMask; if (m == null) return "LastMask=null (아직 버스트가 스폰되지 않았다)"; return "prefab=" + WL.Combat.WeaponTrailDriver.LastMaskPrefab + " " + m.DumpState(); } }