// WL888_Probe.cs — PD 지시 #888 · #889 시험(Play 중) // FxTry : 방패 치기 Slash 래퍼를 PC 앞에 회전 후보로 띄운다(파일 무수정) // Timeline: 슬롯 스킬 시험 시전 → 0.1 s(게임 시간)마다 동작 · 상태 · 위치 · 보이는 렌더러 · 타격 수를 파일에(run_script 마다 새 어셈블리 · K-DEV-023) // Dodge2 : 더블 탭 흉내(WLDoubleTapDodge.Down/Up 직접 호출 · PC 화면 위치 + 픽셀 오프셋) → 0.9 s 뒤 이동 방향을 파일에 using System.Collections; using System.Text; using UnityEngine; public static class WL888_Probe { static readonly string[] States = { "dashslash", "slashflurry_b1", "slashflurry_a1", "slashflurry_b2", "shieldattack", "idle", "run", "dodge", "attack", "Attack2", "Attack3", "hit" }; public static object FxTry(float ex, float ey, float ez, float fwd, float up, float scale) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var p = Resources.Load("WL/Fx/WL_Fx_ShieldSlash"); if (p == null) return "래퍼 없음"; var f = pc.transform.forward; f.y = 0f; f.Normalize(); var rot = Quaternion.LookRotation(f) * Quaternion.Euler(ex, ey, ez); var old = GameObject.Find("WL888_FxTry"); if (old != null) Object.Destroy(old); var go = Object.Instantiate(p, pc.transform.position + f * fwd + Vector3.up * up, rot); go.name = "WL888_FxTry"; foreach (var ps in go.GetComponentsInChildren(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; m.simulationSpeed = 0.15f; } go.transform.localScale = Vector3.one * scale; Object.Destroy(go, 6f); return "띄움 euler(" + ex + "," + ey + "," + ez + ")"; } public static object Timeline(string path, int slot, float seconds) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var arr = typeof(PCActor).GetField("arr_magicData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public).GetValue(pc) as SkillListTableData[]; var data = arr != null && slot >= 0 && slot < arr.Length ? arr[slot] : null; if (data == null) return "슬롯 비어 있음 " + slot; System.IO.File.WriteAllText(path, "skill " + data.n_SkillID + " " + data.e_Skill + " anim=" + data.s_Animation + " range=" + data.f_SkillRange + "\n"); pc.StartCoroutine(Co(pc, data, slot, path, seconds)); return "started " + data.n_SkillID; } static IEnumerator Co(PCActor pc, SkillListTableData data, int slot, string path, float seconds) { int h0 = WL.Combat.Core.CombatEvents.HitConfirmed.Raised; var start = pc.transform.position; int skillHits = 0; int sid = data.n_SkillID; WL.Combat.Core.CombatHandler hh = (in WL.Combat.Core.HitConfirmedEvent e) => { if (e.projectile != null && e.projectile.m_SkillListTableData != null && e.projectile.m_SkillListTableData.n_SkillID == sid) skillHits++; }; WL.Combat.Core.CombatEvents.HitConfirmed.Add(hh); bool ok = pc.Use_Skill(data, true, slot, true); Append(path, "cast=" + ok); float t = 0f, next = 0f; while (t < seconds && pc != null) { if (t >= next) { next += 0.1f; Append(path, Line(pc, t, h0, start)); } yield return null; t += Time.deltaTime; } WL.Combat.Core.CombatEvents.HitConfirmed.Remove(hh); Append(path, "END " + Line(pc, t, h0, start) + " | 이 스킬 타격 " + skillHits + " | flurry " + WL.Combat.Weapon.WLSlashFlurry.LastLog + " windows=" + WL.Combat.Weapon.WLSlashFlurry.Windows + " | vfx swings=" + WL.Combat.Weapon.WLSkillVfx.Swings + " hits=" + WL.Combat.Weapon.WLSkillVfx.Hits); } static string Line(PCActor pc, float t, int h0, Vector3 start) { var an = pc.m_animation; string st = "?"; if (an != null) { var si = an.GetCurrentAnimatorStateInfo(0); foreach (var n in States) if (si.IsName(n)) { st = n; break; } if (an.IsInTransition(0)) { var ni = an.GetNextAnimatorStateInfo(0); foreach (var n in States) if (ni.IsName(n)) { st += ">" + n; break; } } } int on = 0, all = 0; foreach (var r in pc.GetComponentsInChildren(true)) { if (r is ParticleSystemRenderer) continue; all++; if (r.enabled && r.gameObject.activeInHierarchy) on++; } var p = pc.transform.position; var d = p - start; d.y = 0f; string clip = "-"; if (an != null) { var ci = an.GetCurrentAnimatorClipInfo(0); if (ci.Length > 0) clip = ci[0].clip.name; } return "t=" + t.ToString("F2") + " anim=" + pc.Get_CurAnim() + " state=" + st + " clip=" + clip + " moved=" + d.magnitude.ToString("F2") + " vis=" + on + "/" + all + " hits+" + (WL.Combat.Core.CombatEvents.HitConfirmed.Raised - h0) + " stop=" + IsStop(pc) + " step=" + pc.WL_CanAttackStep() + " apply=" + WL.Combat.AttackRootMotion.ApplyCount; } static string IsStop(PCActor pc) { var bf = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public; for (var t = pc.GetType(); t != null; t = t.BaseType) { var f = t.GetField("IsStop", bf); if (f != null) return f.GetValue(pc).ToString(); var p = t.GetProperty("IsStop", bf); if (p != null) return p.GetValue(pc, null).ToString(); } return "?"; } /// PC 를 (x, z) 근처 NavMesh 위로 옮긴다(시험 자리 — 섬 가장자리 · 물가 회피). public static object WarpNear(float x, float z) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; UnityEngine.AI.NavMeshHit hit; if (!UnityEngine.AI.NavMesh.SamplePosition(new Vector3(x, pc.transform.position.y, z), out hit, 6f, UnityEngine.AI.NavMesh.AllAreas)) return "NavMesh 없음"; var ag = pc.GetComponent(); if (ag != null && ag.enabled) ag.Warp(hit.position); else pc.transform.position = hit.position; return "옮김 " + hit.position.ToString("F1"); } public static object Dodge2(string path, float dxPx, float dyPx) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var cam = Camera.main; if (cam == null) return "카메라 없음"; var sp = (Vector2)cam.WorldToScreenPoint(pc.transform.position) + new Vector2(dxPx, dyPx); var ray = cam.ScreenPointToRay(sp); var plane = new Plane(Vector3.up, pc.transform.position); float e; Vector3 expect = plane.Raycast(ray, out e) ? ray.GetPoint(e) - pc.transform.position : Vector3.zero; expect.y = 0f; float now = Time.unscaledTime; WL.Combat.WLDoubleTapDodge.Down(sp, now, 0); WL.Combat.WLDoubleTapDodge.Up(sp, now + 0.05f, false); var before = pc.transform.position; WL.Combat.WLDoubleTapDodge.Down(sp, now + 0.18f, 0); WL.Combat.WLDoubleTapDodge.Up(sp, now + 0.22f, false); System.IO.File.WriteAllText(path, "탭 화면 " + sp.ToString("F0") + " 기대 방향 " + expect.normalized.ToString("F2") + " | " + WL.Combat.WLDoubleTapDodge.LastLog + " anim=" + pc.Get_CurAnim() + "\n"); pc.StartCoroutine(CoAfter(pc, path, before, expect)); return "taps " + WL.Combat.WLDoubleTapDodge.Taps + " double " + WL.Combat.WLDoubleTapDodge.DoubleTaps + " dodges " + WL.Combat.WLDoubleTapDodge.Dodges + " blocked " + WL.Combat.WLDoubleTapDodge.Blocked; } static IEnumerator CoAfter(PCActor pc, string path, Vector3 before, Vector3 expect) { float t = 0f; while (t < 0.9f) { t += Time.deltaTime; yield return null; } var d = pc.transform.position - before; d.y = 0f; float cos = d.sqrMagnitude > 1e-4f && expect.sqrMagnitude > 1e-4f ? Vector3.Dot(d.normalized, expect.normalized) : 0f; Append(path, "0.9 s 뒤 이동 " + d.magnitude.ToString("F2") + " m 방향 " + d.normalized.ToString("F2") + " · 기대와 cos " + cos.ToString("F2") + " · anim=" + pc.Get_CurAnim()); } static void Append(string path, string line) { try { System.IO.File.AppendAllText(path, line + "\n"); } catch { } } }