Project_WL/AgentScripts/WL899_Probe.cs

449 lines
29 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<MobActor>(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<WL.Combat.SlashArcFollower>(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 "이동 시험 대기 시작";
}
/// <summary>휘두르는 도중(WeaponTrailDriver 스윙 창 열림)에 이동을 넣고 스윙 창이 바로 닫히는지 · 그 뒤 호 이펙트가 새로 안 뜨는지 기록.</summary>
public static object MoveInSwing(float seconds)
{
var pc = MyValue.MyPC as PCActor;
if (pc == null) return "PC 없음";
var drv = pc.GetComponentInChildren<WL.Combat.WeaponTrailDriver>(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<WL.Combat.SlashArcFollower>(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 "스윙 중 이동 시험 대기 시작";
}
/// <summary>주력 스킬을 쏘고 sec 초 동안 켜진 파티클 렌더러를 최상위 이펙트 이름별로 모아 가장 컸던 지름(월드 bounds XZ)을 기록.</summary>
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<string, float>();
var firstAt = new System.Collections.Generic.Dictionary<string, float>();
float t0 = Time.time;
UnityEngine.Events.UnityAction act = null;
act = () =>
{
foreach (var r in Object.FindObjectsByType<ParticleSystemRenderer>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{
if (r == null || !r.enabled) continue;
var ps = r.GetComponent<ParticleSystem>(); 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<System.Collections.Generic.KeyValuePair<string, float>>(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<WL.Combat.Core.ComboTracker>() : null; return ct != null ? ct.LastStage : -1; }
/// <summary>검기(호 · 찌르기) 배치마다 PC 중심에서 가장 먼 수평 거리 · 정면 투영 거리를 기록(sec 초).</summary>
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 "검기 기록 시작";
}
/// <summary>해머 — 콤보 1단계 시작 때 적 거리 · 지면 충격파 자리(PC 중심에서 수평 · 정면) 기록(sec 초).</summary>
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 "해머 기록 시작";
}
/// <summary>몹을 dist 에 세우고 주력 스킬 쿨 · 자동 간격을 0 으로 만든 뒤 sec 초 동안 상태 · 거리 · 대쉬 · 대쉬 시전 수를 기록(대쉬 후 방패 치기 확인).</summary>
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 ──
/// <summary>해머 기본 공격 판정 상자(Projectile_10501)의 PC 정면 최대 도달 · 3타 넉백/기절 · 점프 스킬 카운터를 sec 초 기록.</summary>
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<int>();
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<ProjectileBase>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{
if (pb == null || !pb.name.StartsWith("Projectile_1050")) continue;
var bc = pb.GetComponent<BoxCollider>(); 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; } } }
/// <summary>PC 를 멈추게(stopPc) 하고 시험 몹의 상태 · 거리 · 정지 거리 · 루트모션을 상태가 바뀔 때마다 기록(sec 초).</summary>
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");
}
/// <summary>몹 ID(표 n_MonsterID)로 골라 MobWatch 와 같은 기록(없으면 섬 시험 몹 규칙을 끄고 꺼진 몹을 깨운다).</summary>
public static object MobWatchId(int id, float dist, float seconds, bool stopPc)
{
MobActor pick = null;
System.Func<MobActor, int> idOf = m => { var tab = F(m, "m_MonsterTableData") as MonsterTableData; return tab != null ? tab.n_MonsterID : 0; };
foreach (var m in Object.FindObjectsByType<MobActor>(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;
/// <summary>#904 콤보 단계마다 PC 전진(위치 변화) · 표 전진 합(AttackRootMotion.SwingAccum) · 적 거리 · 지금 클립 이름 기록.</summary>
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 "단계 전진 기록 시작";
}
/// <summary>#906 해머 3타 넉백 — 반응이 걸린 순간의 몹 위치와 0.6 초 뒤 위치 차이 · 모델 최대 높이.</summary>
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<Skill_JumpAttack>(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();
}
}