// WL916_Probe.cs — PD 지시 #916 시험(Play 중 · 섬) // Bow() : 직업 · 애니메이터 컨트롤러 · 상태(이름 · 클립 · 속도 · 길이 · 이벤트) — 활 관련 상태만 // Ring() : 사거리 원 상태(반지름 · 표시 · 공격 거리) // Rapid(seconds) : 주력(Q) 발동 → 프레임마다 상태 · 클립 · 속도 · PC 이동 · 화살 수 · 과녁 위치(대상 중심 · 카메라 쪽 거리) · 크기 // Basic(seconds) : 스킬 없이 기본 공격 동안 PC 이동(뒤로 밀림 여부) // Log() using System.Text; using UnityEngine; public static class WL916_Probe { const string Key = "wl916log"; 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; } public static object Log() { return Buf().ToString(); } static Actor Target(PCActor pc, float dist) { Actor d = WL.IslandMob.WLTrailTest.Dummy; if (d == null || !d.isActiveAndEnabled) { float bd = float.MaxValue; foreach (var m in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { if (m == null || m.IsDead()) continue; float dd = Vector3.Distance(pc.Get_position(), m.Get_position()); if (dd < bd) { bd = dd; d = m; } } } if (d != null && dist > 0f) { var f = pc.transform.forward; f.y = 0f; 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.Change_Target(true, d); } return d; } public static object Bow() { var pc = MyValue.MyPC as PCActor; if (pc == null || pc.m_animation == null) return "PC 없음"; var sb = new StringBuilder(); var fId = typeof(Actor).GetField("m_ID", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public); sb.AppendLine("직업 " + (fId != null ? fId.GetValue(pc) : "?") + " · 공격 거리 " + pc.WL_AttackRange.ToString("F2") + " · 컨트롤러 " + pc.m_animation.runtimeAnimatorController.name); var ac = pc.m_animation.runtimeAnimatorController as UnityEditor.Animations.AnimatorController; if (ac == null) { var ov = pc.m_animation.runtimeAnimatorController as AnimatorOverrideController; if (ov != null) { ac = ov.runtimeAnimatorController as UnityEditor.Animations.AnimatorController; sb.AppendLine("오버라이드 → " + (ac != null ? ac.name : "-")); } } if (ac == null) return sb.Append("AnimatorController 아님").ToString(); foreach (var cs in ac.layers[0].stateMachine.states) { var s = cs.state; var clip = s.motion as AnimationClip; string n = s.name.ToLower(); if (!(n.Contains("attack") || n.Contains("skill") || n.Contains("shot") || n.Contains("idle"))) continue; sb.Append(" ").Append(s.name).Append(" · ").Append(clip != null ? clip.name + " " + clip.length.ToString("F2") + "s" : (s.motion != null ? s.motion.name : "-")).Append(" · 속도 ").Append(s.speed.ToString("F2")); if (clip != null) foreach (var ev in clip.events) sb.Append(" · ").Append(ev.functionName).Append("@").Append(ev.time.ToString("F2")); foreach (var tr in s.transitions) sb.Append(" → ").Append(tr.destinationState != null ? tr.destinationState.name : "-").Append(tr.hasExitTime ? "(" + tr.exitTime.ToString("F2") + ")" : ""); sb.AppendLine(); } return sb.ToString(); } public static object Ring() { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var s = WL.Combat.RangeRing.WLRangeRingSettings.Instance; return "원 반지름 " + WL.Combat.RangeRing.WLRangeRing.LastRadius.ToString("F2") + " · 표시 " + WL.Combat.RangeRing.WLRangeRing.Visible + " · 알파 " + WL.Combat.RangeRing.WLRangeRing.Alpha.ToString("F2") + " · 기준 " + WL.Combat.RangeRing.WLRangeRing.LastBaseStop.ToString("F2") + " · 공격 거리 " + pc.WL_AttackRange.ToString("F2") + " · 직업 반지름 " + (s != null ? s.RadiusFor(pc).ToString("F2") : "-") + " · 선만 " + (s != null && s.ringOnly) + " · 마지막 사거리 안 " + WL.Combat.RangeRing.WLRangeRing.LastInRangeTime.ToString("F1") + " · 지금 " + Time.time.ToString("F1") + " · " + WL.Combat.RangeRing.WLRangeRing.LastInfo; } public static object Rapid(float dist, float seconds) { return RapidP(dist, seconds, 0f); } public static object RapidP(float dist, float seconds, float pauseAt) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var d = Target(pc, dist); if (d == null) return "몹 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time; Vector3 p0 = pc.Get_position(); string last = ""; int arrows0 = Count(); bool paused = false; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || pc.m_animation == null) { Application.onBeforeRender -= act; return; } float t = Time.time - t0; var a = pc.m_animation; var si = a.GetCurrentAnimatorStateInfo(0); var ci = a.GetCurrentAnimatorClipInfo(0); string clip = ci.Length > 0 && ci[0].clip != null ? ci[0].clip.name : "-"; string tgt = "-"; var tg = GameObject.Find("Effect_BulletTarget(Clone)"); if (tg != null && tg.activeInHierarchy && d != null) { var cam = Camera.main; var mid = d.Get_Center_position(); float toCam = cam != null ? Vector3.Dot(tg.transform.position - mid, (cam.transform.position - mid).normalized) : 0f; tgt = "과녁 중심차 " + Vector3.Distance(tg.transform.position, mid).ToString("F2") + " 카메라쪽 " + toCam.ToString("F2") + " 배율 " + tg.transform.lossyScale.x.ToString("F2"); } string key = clip + " " + pc.Get_CurAnim() + " sp" + a.speed.ToString("F1") + " 화살 +" + (Count() - arrows0); if (key != last || Mathf.Repeat(t, 0.25f) < Time.deltaTime) { last = key; var mv = pc.Get_position() - p0; mv.y = 0f; var fw = pc.transform.forward; fw.y = 0f; fw.Normalize(); sb.AppendFormat("{0:F2} {1} n={2:F2} · 이동 {3:F2} m(앞 {4:F2}) · {5}", t, key, si.normalizedTime, mv.magnitude, Vector3.Dot(mv, fw), tgt).Append('\n'); } if (!paused && pauseAt > 0f && t >= pauseAt) { paused = true; UnityEditor.EditorApplication.isPaused = true; var cam = Camera.main; var vp = cam != null && d != null ? cam.WorldToViewportPoint(d.Get_Center_position()) : Vector3.zero; bool vis = false; if (d != null) foreach (var r in d.GetComponentsInChildren(false)) if (r.enabled && r.isVisible) { vis = true; break; } int glowOn = 0; foreach (var r in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) if (r.name == "[WL] Glow" && r.enabled) glowOn++; sb.AppendFormat("■ 일시 정지 · 몹 {0} 보임 {1} · 몹 화면 {2:F3},{3:F3} · PC 화면 {4:F3},{5:F3} · 활 빛 {6} · 모으기 {7} · ", d != null ? d.name : "-", vis, vp.x, 1f - vp.y, cam != null ? cam.WorldToViewportPoint(pc.Get_Center_position()).x : 0f, cam != null ? 1f - cam.WorldToViewportPoint(pc.Get_Center_position()).y : 0f, glowOn, GameObject.Find("WL_Fx_BowCharge(Clone)") != null).Append(MarkBounds()).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; sb.AppendFormat("끝 · 몹 크기(몸 반지름) {0:F2} · 몹 키 {1:F2}", WL.Combat.WLCombatMotionSettings.BodyRadius(d), d.Get_Top_postion().y - d.Get_position().y).Append('\n'); } }; Application.onBeforeRender += act; return "Q: " + WL.Combat.Weapon.WLSkillHotkey.Fire(1); } static string MarkBounds() { GameObject go = null; foreach (var t in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) if (t != null && t.name.StartsWith("Effect_BulletTarget")) { go = t.gameObject; break; } if (go == null) return "과녁 없음"; Bounds b = new Bounds(); bool has = false; var sb = new StringBuilder(); foreach (var r in go.GetComponentsInChildren(false)) { var ps = r.GetComponent(); if (ps == null || ps.particleCount == 0) continue; if (!has) { b = r.bounds; has = true; } else b.Encapsulate(r.bounds); sb.Append(ps.name).Append(" ").Append(r.bounds.size.x.ToString("F2")).Append(" "); } return "과녁 " + go.name + " 배율 " + go.transform.lossyScale.x.ToString("F2") + " · 전체 경계 " + (has ? b.size.ToString("F2") : "-") + " · " + sb; } /// 과녁(Effect_BulletTarget) 파티클이 살아 있는 동안 렌더러 경계 크기 · 배율 · 파티클 크기 모드. public static object Mark() { var go = GameObject.Find("Effect_BulletTarget(Clone)"); if (go == null || !go.activeInHierarchy) return "과녁 없음"; var sb = new StringBuilder(); Bounds b = new Bounds(); bool has = false; foreach (var r in go.GetComponentsInChildren(false)) { var ps = r.GetComponent(); if (ps == null || ps.particleCount == 0) continue; if (!has) { b = r.bounds; has = true; } else b.Encapsulate(r.bounds); sb.Append(ps.name).Append(" n ").Append(ps.particleCount).Append(" 경계 ").Append(r.bounds.size.ToString("F2")).Append(" 모드 ").Append(ps.main.scalingMode).Append(" · "); } return "과녁 배율 " + go.transform.lossyScale.x.ToString("F2") + " · 전체 경계 " + (has ? b.size.ToString("F2") : "-") + " · " + sb; } static int s_arrows; static int Count() { int n = 0; foreach (var pb in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) if (pb != null && pb.name.Contains("Arrow")) n++; if (n > s_arrows) s_arrows = n; return n; } /// #916 검 각성 걸음 전진 — 각성(E) 뒤 pushAt 초에 몹을 PC 반대쪽으로 pushDist m 옮겨(크게 밀린 상황) 프레임마다 PC-몹 거리 · 걸음 수 · 명중. public static object SwordFollow(float pushAt, float pushDist, float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var d = Target(pc, 1.5f); if (d == null) return "몹 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time; bool pushed = false; int st0 = WL.Combat.Weapon.WLSlashFlurry.Steps, w0 = WL.Combat.Weapon.WLSlashFlurry.Windows; string last = ""; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || d == null) { Application.onBeforeRender -= act; return; } float t = Time.time - t0; if (!pushed && t >= pushAt) { pushed = true; var away = d.Get_position() - pc.Get_position(); away.y = 0f; away.Normalize(); var np = d.Get_position() + away * pushDist; var ag = d.Get_Agent(); if (ag != null && ag.isActiveAndEnabled && ag.isOnNavMesh) ag.Warp(np); d.transform.position = np; sb.AppendFormat("{0:F2} ▶ 몹을 {1:F1} m 뒤로", t, pushDist).Append('\n'); } var ci = pc.m_animation.GetCurrentAnimatorClipInfo(0); string clip = ci.Length > 0 && ci[0].clip != null ? ci[0].clip.name : "-"; string key = clip + " 걸음+" + (WL.Combat.Weapon.WLSlashFlurry.Steps - st0) + " 창+" + (WL.Combat.Weapon.WLSlashFlurry.Windows - w0); if (key != last || Mathf.Repeat(t, 0.1f) < Time.deltaTime) { last = key; var dv = d.Get_position() - pc.Get_position(); dv.y = 0f; sb.AppendFormat("{0:F2} {1} {2} · PC-몹 {3:F2} m", t, key, pc.Get_CurAnim(), dv.magnitude).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; sb.AppendFormat("끝 · 걸음 +{0} · 전진 누적 {1:F2} m", WL.Combat.Weapon.WLSlashFlurry.Steps - st0, WL.Combat.Weapon.WLSlashFlurry.StepDistance).Append('\n'); } }; Application.onBeforeRender += act; return "E: " + WL.Combat.Weapon.WLSkillHotkey.Fire(2); } /// slot 스킬 발동 뒤 seconds 동안 PC 수평 이동(대상 반대쪽 = 뒤로) 최대 · 끝 거리. public static object SlotMove(int slot, float dist, float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var d = Target(pc, dist); var sb = Buf(); sb.Length = 0; float t0 = Time.time; Vector3 p0 = pc.Get_position(); var fw = pc.transform.forward; fw.y = 0f; fw.Normalize(); float maxBack = 0f; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null) { Application.onBeforeRender -= act; return; } var mv = pc.Get_position() - p0; mv.y = 0f; float back = -Vector3.Dot(mv, fw); if (back > maxBack) maxBack = back; if (Time.time - t0 > seconds) { Application.onBeforeRender -= act; sb.AppendFormat("끝 · 뒤로 최대 {0:F2} m · 끝 이동 {1:F2} m · 넉백 생략 {2}", maxBack, mv.magnitude, WL.Combat.Weapon.WLRapidShot.RecoilSkipped).Append('\n'); } }; Application.onBeforeRender += act; return "slot" + slot + ": " + WL.Combat.Weapon.WLSkillHotkey.Fire(slot); } public static object Basic(float dist, float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var d = Target(pc, dist); if (d == null) return "몹 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time; Vector3 p0 = pc.Get_position(); string last = ""; Vector3 pPrev = p0; float back = 0f; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || pc.m_animation == null) { Application.onBeforeRender -= act; return; } float t = Time.time - t0; var a = pc.m_animation; var ci = a.GetCurrentAnimatorClipInfo(0); string clip = ci.Length > 0 && ci[0].clip != null ? ci[0].clip.name : "-"; var to = d != null ? d.Get_position() - pc.Get_position() : pc.transform.forward; to.y = 0f; to.Normalize(); var step = pc.Get_position() - pPrev; step.y = 0f; float away = -Vector3.Dot(step, to); if (away > 0f && pc.Get_CurAnim() == eAnim.Attack) back += away; pPrev = pc.Get_position(); string key = clip + " " + pc.Get_CurAnim(); if (key != last) { last = key; sb.AppendFormat("{0:F2} {1} · 몹 거리 {2:F2} · 공격 중 뒤로 누적 {3:F2} m", t, key, d != null ? Vector3.Distance(pc.Get_position(), d.Get_position()) : -1f, back).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; sb.AppendFormat("끝 · 공격 중 뒤로 누적 {0:F2} m", back).Append('\n'); } }; Application.onBeforeRender += act; return "기본 공격 관찰 " + seconds + " 초"; } }