// WL899_Probe.cs — PD 지시 #899 시험(Play 중 · 섬) // Cast(dist, sec) : 시험 몹을 PC 정면 dist(m)에 세우고 주력 스킬(Q 경로 = WLSkillHotkey.Fire)을 쏜 뒤 sec 초 동안 매 프레임 기록(상태 · 정규화 시간 · 재생 배속 · 몹 거리) // Move(sec) : 검기 호 이펙트가 떠 있는 프레임을 기다렸다가 조이스틱 이동(상태 1 + Run_byJoystick)을 sec 초 넣고 호 이펙트가 꺼지는지 기록 // Log() : 위 기록 · Hammer() : 강력한 일격 이펙트 크기 노드 배율 · 판정 상자 using System.Text; using UnityEngine; public static class WL899_Probe { const string Key = "wl899log"; static StringBuilder Buf() { var sb = System.AppDomain.CurrentDomain.GetData(Key) as StringBuilder; if (sb == null) { sb = new StringBuilder(); System.AppDomain.CurrentDomain.SetData(Key, sb); } return sb; } static Actor Dummy() { var d = WL.IslandMob.WLTrailTest.Dummy; if (d != null && d.isActiveAndEnabled) return d; var pc = MyValue.MyPC; Actor best = null; float bd = float.MaxValue; foreach (var m in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { if (m == null || m.IsDead()) continue; float dd = pc != null ? Vector3.Distance(pc.Get_position(), m.Get_position()) : 0f; if (dd < bd) { bd = dd; best = m; } } return best; } static string StateName(Animator a, AnimatorStateInfo st) { foreach (var n in new[] { "shieldattack", "shieldbash", "idle", "run", "jump", "attack", "Attack2", "Attack3", "dash" }) if (st.IsName(n)) return n; return "#" + st.shortNameHash; } public static object Cast(float dist, float seconds) { var pc = MyValue.MyPC; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; if (dist > 0f) { var f = pc.transform.forward; f.y = 0f; if (f.sqrMagnitude < 1e-4f) f = Vector3.forward; f.Normalize(); var want = pc.Get_position() + f * dist; UnityEngine.AI.NavMeshHit hit; if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 1.5f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position; d.Stop(true); d.Set_Warp(want); } var sb = Buf(); sb.Length = 0; float t0 = Time.time, acc0 = WL.Combat.AttackRootMotion.SwingAccum; Vector3 p0 = pc.Get_position(); sb.Append("시작 거리 ").Append(Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2")).Append('\n'); UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || pc.m_animation == null || d == null) { Application.onBeforeRender -= act; return; } var a = pc.m_animation; var st = a.GetCurrentAnimatorStateInfo(0); sb.AppendFormat("{0:F2} {1} n={2:F2} sp={3:F2} d={4:F2} tr={5} {6}\n", Time.time - t0, StateName(a, st), st.normalizedTime, a.speed, Vector3.Distance(pc.Get_position(), d.Get_position()), a.IsInTransition(0) ? 1 : 0, pc.Get_CurAnim()); if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; sb.Append("끝 · PC 이동 ").Append(Vector3.Distance(p0, pc.Get_position()).ToString("F2")).Append(" m · 표 전진 합 ") .Append((WL.Combat.AttackRootMotion.SwingAccum - acc0).ToString("F2")).Append(" m\n"); } }; Application.onBeforeRender += act; string r = WL.Combat.Weapon.WLSkillHotkey.Fire(); return "발동: " + r + " · 거리 " + Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2"); } public static object Move(float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; int k0 = WL.Combat.SlashArcFollower.KillOnMoveCount; float t0 = Time.time, moveStart = -1f; int arcAtStart = -1; var dir = pc.transform.right; dir.y = 0f; dir.Normalize(); UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null) { Application.onBeforeRender -= act; return; } int arcs = 0; foreach (var f in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) if (f.isActiveAndEnabled) arcs++; if (moveStart < 0f) { if (arcs > 0) { moveStart = Time.time; arcAtStart = arcs; pc.Set_JoystickStatus(1); sb.Append("호 이펙트 ").Append(arcs).Append("개 떠 있을 때 이동 시작 · ").Append(pc.Get_CurAnim()).Append('\n'); } else if (Time.time - t0 > 6f) { Application.onBeforeRender -= act; sb.Append("6초 안에 호 이펙트가 안 떠서 중단\n"); } return; } pc.Run_byJoystick(dir); sb.AppendFormat("{0:F2} 이동 중 · 호 {1} · 소멸 누적 +{2} · 상태 {3}\n", Time.time - moveStart, arcs, WL.Combat.SlashArcFollower.KillOnMoveCount - k0, pc.JoystickStatus); if (Time.time - moveStart > seconds) { pc.Set_JoystickStatus(3); pc.Set_AutoBraking(true); Application.onBeforeRender -= act; sb.Append("이동 끝(상태 ").Append(pc.JoystickStatus).Append(")\n"); } }; Application.onBeforeRender += act; return "이동 시험 대기 시작"; } /// 휘두르는 도중(WeaponTrailDriver 스윙 창 열림)에 이동을 넣고 스윙 창이 바로 닫히는지 · 그 뒤 호 이펙트가 새로 안 뜨는지 기록. public static object MoveInSwing(float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var drv = pc.GetComponentInChildren(true); var fSwing = typeof(WL.Combat.WeaponTrailDriver).GetField("_swingActive", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (drv == null || fSwing == null) return "궤적 드라이버/필드 없음 drv=" + (drv != null); var sb = Buf(); sb.Length = 0; float t0 = Time.time, moveStart = -1f; var dir = pc.transform.right; dir.y = 0f; dir.Normalize(); UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || drv == null) { Application.onBeforeRender -= act; return; } bool swing = (bool)fSwing.GetValue(drv); int arcs = 0; foreach (var f in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) if (f.isActiveAndEnabled) arcs++; if (moveStart < 0f) { if (swing) { moveStart = Time.time; pc.Set_JoystickStatus(1); sb.Append("스윙 중(호 ").Append(arcs).Append("개) 이동 시작 · ").Append(pc.Get_CurAnim()).Append('\n'); } else if (Time.time - t0 > 6f) { Application.onBeforeRender -= act; sb.Append("6초 안에 스윙 창을 못 잡아 중단\n"); } return; } pc.Run_byJoystick(dir); sb.AppendFormat("{0:F2} 이동 중 · 스윙 창 {1} · 호 {2}\n", Time.time - moveStart, swing ? "열림" : "닫힘", arcs); if (Time.time - moveStart > seconds) { pc.Set_JoystickStatus(3); pc.Set_AutoBraking(true); Application.onBeforeRender -= act; sb.Append("이동 끝\n"); } }; Application.onBeforeRender += act; return "스윙 중 이동 시험 대기 시작"; } /// 주력 스킬을 쏘고 sec 초 동안 켜진 파티클 렌더러를 최상위 이펙트 이름별로 모아 가장 컸던 지름(월드 bounds XZ)을 기록. public static object CastFx(float dist, float seconds) { var pc = MyValue.MyPC; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; if (dist > 0f) { var f = pc.transform.forward; f.y = 0f; if (f.sqrMagnitude < 1e-4f) f = Vector3.forward; f.Normalize(); var want = pc.Get_position() + f * dist; UnityEngine.AI.NavMeshHit hit; if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 1.5f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position; d.Stop(true); d.Set_Warp(want); } var sb = Buf(); sb.Length = 0; var maxSize = new System.Collections.Generic.Dictionary(); var firstAt = new System.Collections.Generic.Dictionary(); float t0 = Time.time; UnityEngine.Events.UnityAction act = null; act = () => { foreach (var r in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { if (r == null || !r.enabled) continue; var ps = r.GetComponent(); if (ps == null || ps.particleCount == 0) continue; var root = r.transform.root; string key = root.name + "/" + r.name; if (pc != null && root == pc.transform.root) key = "(PC)/" + r.name; var b = r.bounds; float sz = Mathf.Max(b.size.x, b.size.z); if (Vector3.Distance(new Vector3(b.center.x, 0, b.center.z), new Vector3(pc.Get_position().x, 0, pc.Get_position().z)) > 6f) continue; float cur; if (!maxSize.TryGetValue(key, out cur) || sz > cur) maxSize[key] = sz; if (!firstAt.ContainsKey(key)) firstAt[key] = Time.time - t0; } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; var list = new System.Collections.Generic.List>(maxSize); list.Sort((a, b) => b.Value.CompareTo(a.Value)); foreach (var kv in list) sb.Append(kv.Value.ToString("F1")).Append(" m @").Append(firstAt[kv.Key].ToString("F2")).Append("s ").Append(kv.Key).Append('\n'); } }; Application.onBeforeRender += act; return "발동: " + WL.Combat.Weapon.WLSkillHotkey.Fire(); } // ── #900 ────────────────────────────────────────────────────────────── static int StageOf(PCActor pc) { var ct = pc != null ? pc.GetComponent() : null; return ct != null ? ct.LastStage : -1; } /// 검기(호 · 찌르기) 배치마다 PC 중심에서 가장 먼 수평 거리 · 정면 투영 거리를 기록(sec 초). public static object Slash(float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; int last = WL.Combat.WeaponTrailDriver.MappedSpawnCount; float t0 = Time.time; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null) { Application.onBeforeRender -= act; return; } int n = WL.Combat.WeaponTrailDriver.MappedSpawnCount; if (n != last) { last = n; var p = WL.Combat.WeaponTrailDriver.LastPlacement; Vector3 c = pc.Get_position(); var f = pc.transform.forward; f.y = 0f; f.Normalize(); float maxD = 0f, maxF = 0f; if (p.kind == WL.Combat.SwingKind.Stab) { var e = p.stabStartPlaced + p.stabAxisPlaced.normalized * p.stabLenPlaced; foreach (var q in new[] { p.stabStartPlaced, e }) { var v = q - c; v.y = 0f; maxD = Mathf.Max(maxD, v.magnitude); maxF = Mathf.Max(maxF, Vector3.Dot(v, f)); } } else { var b = p.arcBisector.normalized; var side = Vector3.Cross(p.arcNormal.normalized, b).normalized; for (int k = -20; k <= 20; k++) { float th = k / 20f * p.sweepMesh * 0.5f * Mathf.Deg2Rad; var q = p.arcCenter + p.arcRadius * (Mathf.Cos(th) * b + Mathf.Sin(th) * side); var v = q - c; v.y = 0f; maxD = Mathf.Max(maxD, v.magnitude); maxF = Mathf.Max(maxF, Vector3.Dot(v, f)); } } var tg = pc.Get_Target(); sb.AppendFormat("{0:F2}s 단계 {1} {2} {3} · 가장 먼 거리 {4:F2} · 정면 {5:F2} · 반지름 {6:F2} · 적 {7}\n", Time.time - t0, StageOf(pc), p.kind, p.effect, maxD, maxF, p.arcRadius, tg != null ? Vector3.Distance(c, tg.Get_position()).ToString("F2") : "-"); } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; sb.Append("끝 · 원 반지름 ").Append(WL.Combat.RangeRing.WLRangeRing.AttackReach(pc).ToString("F2")).Append('\n'); } }; Application.onBeforeRender += act; return "검기 기록 시작"; } /// 해머 — 콤보 1단계 시작 때 적 거리 · 지면 충격파 자리(PC 중심에서 수평 · 정면) 기록(sec 초). public static object Ground(float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; int g0 = WL.Combat.Impact.WLImpactFx.GroundShown, lastStage = StageOf(pc); float t0 = Time.time; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null) { Application.onBeforeRender -= act; return; } int st = StageOf(pc); var tg = pc.Get_Target(); Vector3 c = pc.Get_position(); if (st != lastStage) { lastStage = st; sb.AppendFormat("{0:F2}s 단계 {1} 시작 · 적 {2}\n", Time.time - t0, st, tg != null ? Vector3.Distance(c, tg.Get_position()).ToString("F2") : "-"); } int g = WL.Combat.Impact.WLImpactFx.GroundShown; if (g != g0) { g0 = g; string s = WL.Combat.Impact.WLImpactFx.LastGround; int i = s.LastIndexOf('@'); var parts = i >= 0 ? s.Substring(i + 2).TrimEnd(')').Split(',') : new string[0]; if (parts.Length == 3) { var q = new Vector3(float.Parse(parts[0]), float.Parse(parts[1]), float.Parse(parts[2])); var v = q - c; v.y = 0f; var f = pc.transform.forward; f.y = 0f; f.Normalize(); var td = tg != null ? tg.Get_position() - c : f; td.y = 0f; td.Normalize(); var mf = pc.m_animation != null ? pc.m_animation.transform.forward : f; mf.y = 0f; mf.Normalize(); sb.AppendFormat("{0:F2}s 충격파 · 수평 {1:F2} · 적 방향 {2:F2} · 루트 정면 {3:F2} · 모델 정면 {4:F2} · 적 {5} · 충격파-적 {6}\n", Time.time - t0, v.magnitude, Vector3.Dot(v, td), Vector3.Dot(v, f), Vector3.Dot(v, mf), tg != null ? Vector3.Distance(c, tg.Get_position()).ToString("F2") : "-", tg != null ? Vector3.Distance(new Vector3(q.x, 0, q.z), new Vector3(tg.Get_position().x, 0, tg.Get_position().z)).ToString("F2") : "-"); } else sb.Append(s).Append('\n'); } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; sb.Append("끝 · 원 ").Append(WL.Combat.RangeRing.WLRangeRing.AttackReach(pc).ToString("F2")).Append(" · 정지 ").Append(WL.Combat.WLCombatMotionSettings.ApproachStop(pc, -1f).ToString("F2")).Append('\n'); } }; Application.onBeforeRender += act; return "해머 기록 시작"; } /// 몹을 dist 에 세우고 주력 스킬 쿨 · 자동 간격을 0 으로 만든 뒤 sec 초 동안 상태 · 거리 · 대쉬 · 대쉬 시전 수를 기록(대쉬 후 방패 치기 확인). public static object Watch(float dist, float seconds) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; var f = pc.transform.forward; f.y = 0f; if (f.sqrMagnitude < 1e-4f) f = Vector3.forward; f.Normalize(); var want = pc.Get_position() + f * dist; UnityEngine.AI.NavMeshHit hit; if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 1.5f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position; d.Stop(true); d.Set_Warp(want); pc.Set_MagicCoolTime(0f, 0); var fa = typeof(PCActor).GetField("t_autoskill", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public); if (fa != null) fa.SetValue(pc, 0f); var sb = Buf(); sb.Length = 0; int dc0 = WL.Combat.Weapon.WLWeaponSkillSlots.DashCasts; float t0 = Time.time; string lastState = ""; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || d == null) { Application.onBeforeRender -= act; return; } var a = pc.m_animation; var st = a.GetCurrentAnimatorStateInfo(0); string sn = StateName(a, st); bool dash = WL.Combat.DashDriver.IsDashingNow(pc); string key = sn + (dash ? "+대쉬" : ""); if (key != lastState) { lastState = key; sb.AppendFormat("{0:F2}s {1} · 거리 {2:F2} · 대쉬 시전 +{3} · {4}\n", Time.time - t0, key, Vector3.Distance(pc.Get_position(), d.Get_position()), WL.Combat.Weapon.WLWeaponSkillSlots.DashCasts - dc0, pc.Get_CurAnim()); } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; sb.Append("끝 · 거리 ").Append(Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2")).Append(" · 마지막 대쉬 시전 ").Append(WL.Combat.Weapon.WLWeaponSkillSlots.LastDashCast).Append(" · ").Append(WL.Combat.DashDriver.LastInfo).Append('\n'); } }; Application.onBeforeRender += act; return "관찰 시작 · 몹 " + Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2") + " m"; } // ── #902 ── /// 해머 기본 공격 판정 상자(Projectile_10501)의 PC 정면 최대 도달 · 3타 넉백/기절 · 점프 스킬 카운터를 sec 초 기록. public static object Hammer902(float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time, maxF = 0f, maxD = 0f; int boxes = 0; int st0 = WL.Combat.Weapon.WLStageHitReaction.Applied, fc0 = WL.Combat.Weapon.WLWeaponSkillSlots.FirstCasts, la0 = WL.Combat.Weapon.WLLaunch.Launches, kb0 = WL.Combat.Weapon.WLLaunch.Knockbacks; var seen = new System.Collections.Generic.HashSet(); UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null) { Application.onBeforeRender -= act; return; } Vector3 c = pc.Get_position(); var f = pc.transform.forward; f.y = 0f; f.Normalize(); foreach (var pb in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { if (pb == null || !pb.name.StartsWith("Projectile_1050")) continue; var bc = pb.GetComponent(); if (bc == null || !bc.enabled) continue; if (seen.Add(pb.GetInstanceID() ^ Time.frameCount)) { } boxes++; var b = bc.bounds; for (int k = 0; k < 8; k++) { var q = b.center + new Vector3((k & 1) == 0 ? -b.extents.x : b.extents.x, 0f, (k & 4) == 0 ? -b.extents.z : b.extents.z); var v = q - c; v.y = 0f; maxF = Mathf.Max(maxF, Vector3.Dot(v, f)); maxD = Mathf.Max(maxD, v.magnitude); } } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; sb.AppendFormat("판정 상자 프레임 {0} · 정면 최대 {1:F2} m · 모서리 최대 {2:F2} m\n", boxes, maxF, maxD); sb.Append("3타 반응 +").Append(WL.Combat.Weapon.WLStageHitReaction.Applied - st0).Append(" · 넉백 +").Append(WL.Combat.Weapon.WLLaunch.Knockbacks - kb0).Append(" · ").Append(WL.Combat.Weapon.WLStageHitReaction.LastLog).Append('\n'); sb.Append("우선 시전 +").Append(WL.Combat.Weapon.WLWeaponSkillSlots.FirstCasts - fc0).Append(" · 띄우기 +").Append(WL.Combat.Weapon.WLLaunch.Launches - la0) .Append(" · 가운데 ").Append(WL.Combat.Weapon.WLSkillVfx.Centers).Append(" · 8방향 ").Append(WL.Combat.Weapon.WLSkillVfx.Radials).Append(" · 원 ").Append(WL.Combat.RangeRing.WLRangeRing.LastRadius.ToString("F2")).Append('\n'); } }; Application.onBeforeRender += act; return "해머 #902 기록 시작"; } // ── #903 몹 AI 관찰 ── static readonly System.Reflection.BindingFlags BF = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public; static object F(object o, string n) { for (var t = o.GetType(); t != null; t = t.BaseType) { var f = t.GetField(n, BF); if (f != null) return f.GetValue(o); } return null; } static void SetF(object o, string n, object v) { for (var t = o.GetType(); t != null; t = t.BaseType) { var f = t.GetField(n, BF); if (f != null) { f.SetValue(o, v); return; } } } /// PC 를 멈추게(stopPc) 하고 시험 몹의 상태 · 거리 · 정지 거리 · 루트모션을 상태가 바뀔 때마다 기록(sec 초). public static object MobWatch(float dist, float seconds, bool stopPc) { var pc = MyValue.MyPC as PCActor; var d = (s_pick != null && s_pick.isActiveAndEnabled ? s_pick : Dummy() as MobActor); s_pick = null; if (pc == null || d == null) return "PC/몹 없음"; if (stopPc) SetF(pc, "IsStop", true); if (dist > 0f) { var f = pc.transform.forward; f.y = 0f; f.Normalize(); d.Set_Warp(pc.Get_position() + f * dist); } var sb = Buf(); sb.Length = 0; float t0 = Time.time; string last = ""; var ag = d.Get_Agent(); sb.Append(d.name).Append(" 공격거리(표)=").Append(F(d, "m_NavStopDistance")).Append(" 선공=").Append(F(d, "b_IsFirstAttack")).Append(" 추적=").Append(F(d, "f_BaseChaseRange")) .Append(" 크기=").Append(d.transform.localScale.x.ToString("F2")).Append(" 루트모션=").Append(d.m_animation != null && d.m_animation.applyRootMotion).Append('\n'); UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || d == null) { Application.onBeforeRender -= act; return; } string key = F(d, "MobStatus") + "/" + d.Get_CurAnim(); float dd = Vector3.Distance(pc.Get_position(), d.Get_position()); if (key != last) { last = key; sb.AppendFormat("{0:F2}s {1} · 거리 {2:F2} · 정지 {3:F2} · 속도 {4:F2} · hitT {5}\n", Time.time - t0, key, dd, ag != null ? ag.stoppingDistance : -1f, ag != null ? ag.velocity.magnitude : -1f, F(d, "m_HitTime")); } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; if (stopPc) SetF(pc, "IsStop", false); sb.AppendFormat("끝 · 거리 {0:F2}\n", dd); } }; Application.onBeforeRender += act; return "몹 관찰 시작 · 거리 " + Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2"); } /// 몹 ID(표 n_MonsterID)로 골라 MobWatch 와 같은 기록(없으면 섬 시험 몹 규칙을 끄고 꺼진 몹을 깨운다). public static object MobWatchId(int id, float dist, float seconds, bool stopPc) { MobActor pick = null; System.Func idOf = m => { var tab = F(m, "m_MonsterTableData") as MonsterTableData; return tab != null ? tab.n_MonsterID : 0; }; foreach (var m in Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) if (m != null && idOf(m) == id) { pick = m; if (m.isActiveAndEnabled && !m.IsDead()) break; } if (pick == null) return "몹 ID " + id + " 없음"; if (!pick.isActiveAndEnabled) { int prev = WL.IslandMob.WLTrailTest.Override; WL.IslandMob.WLTrailTest.Override = 0; try { pick.On_Regen(); } finally { WL.IslandMob.WLTrailTest.Override = prev; } } s_pick = pick; return MobWatch(dist, seconds, stopPc); } static MobActor s_pick; /// #904 콤보 단계마다 PC 전진(위치 변화) · 표 전진 합(AttackRootMotion.SwingAccum) · 적 거리 · 지금 클립 이름 기록. public static object StageMove(float dist, float seconds) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; if (dist > 0f) { var f = pc.transform.forward; f.y = 0f; f.Normalize(); d.Stop(true); d.Set_Warp(pc.Get_position() + f * dist); } var sb = Buf(); sb.Length = 0; float t0 = Time.time; int last = -1; Vector3 p0 = pc.Get_position(); float a0 = WL.Combat.AttackRootMotion.SwingAccum; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || d == null) { Application.onBeforeRender -= act; return; } int st = StageOf(pc); if (st != last) { var ci = pc.m_animation.GetCurrentAnimatorClipInfo(0); string clip = ci.Length > 0 && ci[0].clip != null ? ci[0].clip.name : "-"; sb.AppendFormat("{0:F2}s 단계 {1} 시작 · 적 {2:F2} · 직전 단계 PC 이동 {3:F2} · 표 전진 {4:F2} · 클립 {5}\n", Time.time - t0, st, Vector3.Distance(pc.Get_position(), d.Get_position()), Vector3.Distance(p0, pc.Get_position()), WL.Combat.AttackRootMotion.SwingAccum - a0, clip); last = st; p0 = pc.Get_position(); a0 = WL.Combat.AttackRootMotion.SwingAccum; } if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; d.Stop(false); } }; Application.onBeforeRender += act; return "단계 전진 기록 시작"; } /// #906 해머 3타 넉백 — 반응이 걸린 순간의 몹 위치와 0.6 초 뒤 위치 차이 · 모델 최대 높이. public static object KnockWatch(float seconds) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time; int a0 = WL.Combat.Weapon.WLStageHitReaction.Applied; Vector3 hitPos = Vector3.zero; float hitAt = -1f, maxY = 0f; float baseY = d.m_animation != null ? d.m_animation.transform.localPosition.y : 0f; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || d == null) { Application.onBeforeRender -= act; return; } int a = WL.Combat.Weapon.WLStageHitReaction.Applied; if (a != a0) { a0 = a; if (WL.Combat.Weapon.WLStageHitReaction.LastLog.StartsWith("stage 3")) { hitPos = d.Get_position(); hitAt = Time.time; maxY = 0f; } } if (hitAt > 0f) { if (d.m_animation != null) maxY = Mathf.Max(maxY, d.m_animation.transform.position.y - d.Get_position().y); if (Time.time - hitAt >= 0.6f) { var v = d.Get_position() - hitPos; v.y = 0f; sb.AppendFormat("3타 넉백 · 몹 이동 {0:F2} m · 모델 최대 높이(발 기준) {1:F2} · PC 와 거리 {2:F2}\n", v.magnitude, maxY, Vector3.Distance(pc.Get_position(), d.Get_position())); hitAt = -1f; } } if (Time.time - t0 > seconds) Application.onBeforeRender -= act; }; Application.onBeforeRender += act; return "넉백 관찰 시작"; } public static object Log() { return Buf().ToString(); } public static object Hammer() { var sb = new StringBuilder(); sb.Append("크기 조절 ").Append(WL.Combat.Weapon.WLSkillVfx.Scaled).Append(" · ").Append(WL.Combat.Weapon.WLSkillVfx.LastLog).Append('\n'); foreach (var s in Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) sb.Append(s.name).Append(" 켜짐=").Append(s.gameObject.activeInHierarchy).Append(" 크기 노드 ").Append(s.tf_size != null ? s.tf_size.localScale.ToString("F2") : "-") .Append(" · 판정 상자 ").Append(s.m_boxcollider != null ? s.m_boxcollider.size.ToString("F1") : "-").Append(" × 루트 ").Append(s.transform.localScale.ToString("F2")).Append('\n'); var slots = WL.Combat.Weapon.WLWeaponSkillSlots.LastSlots; sb.Append("무기 스킬 칸 [").Append(slots != null ? string.Join(",", System.Array.ConvertAll(slots, x => x.ToString())) : "-").Append("] 등급 ").Append(WL.Combat.Weapon.WLWeaponSkillSlots.LastTier); return sb.ToString(); } }