// WL908_Probe.cs — PD 지시 #908 시험(Play 중 · 섬) // Setup(group, tier) : 무기 그룹 장착 + 장비 전체 등급(치트) // CastMove(slot, dist, delay, moveSec, sec, angle) : 시험 몹을 PC 정면 dist(m)에 세우고(멈춤) 무기 스킬 slot(1 주력 Q · 2 각성 E)을 쏜 뒤 // delay 초 후 moveSec 초 동안 조이스틱 이동을 넣는다(섬 아바타와 같은 호출 순서 · angle = PC 정면 기준 이동 방향 각도) · sec 초 동안 매 프레임 기록 // Log() : 기록 · Counters() : 관련 카운터 using System.Text; using UnityEngine; public static class WL908_Probe { const string Key = "wl908log"; 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 Clip(Animator a) { if (a == null) return "-"; var ci = a.GetCurrentAnimatorClipInfo(0); string s = ci.Length > 0 && ci[0].clip != null ? ci[0].clip.name : "-"; if (a.IsInTransition(0)) { var ni = a.GetNextAnimatorClipInfo(0); s += "→" + (ni.Length > 0 && ni[0].clip != null ? ni[0].clip.name : "-"); } return s; } public static object Setup(int group, int tier) { string r = ""; if (WLInventoryReady()) foreach (var it in WL.Equip.WLInventory.SortedItems()) if (it != null && it.itemJson != null && it.itemJson.GroupNo == group) { r = "장착 " + group + " " + it.itemJson.Item_name + " → " + WL.Equip.WLInventory.Equip(it.rawItem._id); break; } if (r == "") r = "인벤토리에 없음 " + group; if (tier > 0) r += " · 등급 " + WL.Combat.Weapon.WLWeaponGradeCheat.Apply(tier); return r; } static bool WLInventoryReady() { return WL.Equip.WLInventory.EnsureLoaded(); } public static object CastMove(int slot, float dist, float delay, float moveSec, float seconds, float angle) { 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; 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; bool moving = false, moved = false; string last = ""; int sf0 = WL.Combat.Core.CombatEvents.SkillFired.Raised; Vector3 fwd0 = pc.transform.forward; fwd0.y = 0f; fwd0.Normalize(); Vector3 mdir = Quaternion.Euler(0f, angle, 0f) * fwd0; float y0 = pc.transform.position.y; 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; } float t = Time.time - t0; if (!moved && moveSec > 0f && t >= delay) { if (!moving) { pc.Set_AutoBraking(false); pc.Set_JoystickStatus(1); moving = true; sb.AppendFormat("{0:F2} ▶ 이동 입력 시작\n", t); } pc.Run_byJoystick(mdir); if (t >= delay + moveSec) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); moving = false; moved = true; sb.AppendFormat("{0:F2} ■ 이동 입력 끝\n", t); } } string key = Clip(pc.m_animation) + " " + pc.Get_CurAnim(); if (key != last) { last = key; sb.AppendFormat("{0:F2} {1} · 거리 {2:F2} · 높이 {3:F2} · 조이 {4} · 스킬발사 +{5}\n", t, key, Vector3.Distance(pc.Get_position(), d.Get_position()), pc.transform.position.y - y0, pc.JoystickStatus, WL.Combat.Core.CombatEvents.SkillFired.Raised - sf0); } if (t > seconds) { Application.onBeforeRender -= act; if (moving) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); } sb.AppendFormat("끝 · 거리 {0:F2} · 스킬발사 +{1}\n", Vector3.Distance(pc.Get_position(), d.Get_position()), WL.Combat.Core.CombatEvents.SkillFired.Raised - sf0); } }; Application.onBeforeRender += act; string r = WL.Combat.Weapon.WLSkillHotkey.Fire(slot); return "발동: " + r; } public static object Log() { return Buf().ToString(); } public static object Counters() { return "접근 바로 " + WL.Combat.Weapon.WLSkillApproach.Immediate + " · 대쉬 " + WL.Combat.Weapon.WLSkillApproach.Dashes + " · 도착 " + WL.Combat.Weapon.WLSkillApproach.Arrivals + " · 시간초과 " + WL.Combat.Weapon.WLSkillApproach.Timeouts + " · 끊김 " + WL.Combat.Weapon.WLSkillApproach.Broken + " · " + WL.Combat.Weapon.WLSkillApproach.LastLog + " | 연속베기 " + WL.Combat.Weapon.WLSlashFlurry.Taken + " · 스킬발사 " + WL.Combat.Core.CombatEvents.SkillFired.Raised + " · 핫키 " + WL.Combat.Weapon.WLSkillHotkey.LastLog; } /// 이동 입력을 먼저 넣고(이동 키를 누른 채) preMove 초 뒤에 slot 스킬을 쏜다 · 이동은 moveSec 초 동안 계속 · sec 초 기록. public static object MoveCast(int slot, float dist, float preMove, float moveSec, float seconds, float angle) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; var f0 = pc.transform.forward; f0.y = 0f; if (f0.sqrMagnitude < 1e-4f) f0 = Vector3.forward; f0.Normalize(); if (dist > 0f) { var want = pc.Get_position() + f0 * 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; bool moving = false, fired = false; string last = ""; int sf0 = WL.Combat.Core.CombatEvents.SkillFired.Raised; Vector3 mdir = Quaternion.Euler(0f, angle, 0f) * f0; float y0 = pc.transform.position.y; 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; } float t = Time.time - t0; if (t < moveSec) { if (!moving) { pc.Set_AutoBraking(false); pc.Set_JoystickStatus(1); moving = true; sb.AppendFormat("{0:F2} ▶ 이동 입력 시작", t).Append('\n'); } pc.Run_byJoystick(mdir); } else if (moving) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); moving = false; sb.AppendFormat("{0:F2} ■ 이동 입력 끝", t).Append('\n'); } if (!fired && t >= preMove) { fired = true; sb.AppendFormat("{0:F2} ◆ 발동 ", t).Append(WL.Combat.Weapon.WLSkillHotkey.Fire(slot)).Append('\n'); } string key = Clip(pc.m_animation) + " " + pc.Get_CurAnim(); if (key != last) { last = key; sb.AppendFormat("{0:F2} {1} · 거리 {2:F2} · 높이 {3:F2} · 조이 {4} · 스킬발사 +{5}", t, key, Vector3.Distance(pc.Get_position(), d.Get_position()), pc.transform.position.y - y0, pc.JoystickStatus, WL.Combat.Core.CombatEvents.SkillFired.Raised - sf0).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; if (moving) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); } sb.AppendFormat("끝 · 거리 {0:F2} · 스킬발사 +{1}", Vector3.Distance(pc.Get_position(), d.Get_position()), WL.Combat.Core.CombatEvents.SkillFired.Raised - sf0).Append('\n'); } }; Application.onBeforeRender += act; return "이동 먼저 · " + preMove + " s 뒤 발동"; } /// 시험 몹을 dist 에 세우고(멈춤) 해머 1타가 땅에 닿는 순간(지면 충격 이펙트 표시) 에디터를 일시 정지한다 — 캡처용. public static object PauseOnGround(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; f.Normalize(); d.Stop(true); d.Set_Warp(pc.Get_position() + f * dist); int g0 = WL.Combat.Impact.WLImpactFx.GroundShown; float t0 = Time.time; var sb = Buf(); sb.Length = 0; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || d == null || Time.time - t0 > seconds) { Application.onBeforeRender -= act; return; } if (WL.Combat.Impact.WLImpactFx.GroundShown != g0) { Application.onBeforeRender -= act; sb.Append("정지 · PC-적 ").Append(Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2")).Append(" · ").Append(WL.Combat.Impact.WLImpactFx.LastGround); UnityEditor.EditorApplication.isPaused = true; } }; Application.onBeforeRender += act; return "대기 · 몹 " + Vector3.Distance(pc.Get_position(), d.Get_position()).ToString("F2"); } /// 지금 켜진 이 PC 의 원본 스킬 투사체(InGameInfo 스킬 풀) · 스킬 연출 활성 수 · 이름이 Effect_PCDash 인 켜진 먼지의 방향(대상 방향과의 내적). public static object SkillObjs() { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = new StringBuilder(); var f = typeof(InGameInfo).GetField("dic_Skill", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public); var dic = f != null ? f.GetValue(InGameInfo.Ins) as System.Collections.IDictionary : null; int n = 0; if (dic != null) foreach (System.Collections.DictionaryEntry kv in dic) foreach (var o in (System.Collections.IList)kv.Value) { var go = o as GameObject; if (go == null || !go.activeInHierarchy) continue; var pb = go.GetComponent(); var data = pb != null ? pb.Get_ProjectTileData() : null; if (data != null && ReferenceEquals(data.shooter, pc)) { n++; sb.Append(kv.Key).Append(' '); } } sb.Append("| 켜진 스킬 투사체 ").Append(n).Append(" · 연출 활성 ").Append(WL.Combat.Reaction.SkillSpectacle.ActiveCount) .Append(" · 취소 끈 연출 ").Append(WL.Combat.Reaction.SkillSpectacle.Cancelled); return sb.ToString(); } public static object Cancel() { return "시작 " + WL.Combat.Weapon.WLSkillCancel.Casts + " · 스킵 " + WL.Combat.Weapon.WLSkillCancel.Skips + " · 끈 것 " + WL.Combat.Weapon.WLSkillCancel.SkippedObjects + " · 이동 무시 " + WL.Combat.Weapon.WLSkillCancel.MoveBlocked + " · 구르기 무시 " + WL.Combat.Weapon.WLSkillCancel.DodgeBlocked + " · 미룸 " + WL.Combat.Weapon.WLSkillCancel.BrakeDeferred + " · 적용 " + WL.Combat.Weapon.WLSkillCancel.BrakeApplied + " · 오류 " + WL.Combat.Weapon.WLSkillCancel.Errors + " · " + WL.Combat.Weapon.WLSkillCancel.LastLog; } /// slot 스킬을 쏘고 delay 초 뒤 구르기(원본 Play_Dodge · 옆 angle 도) · sec 초 기록. public static object CastDodge(int slot, float dist, float delay, float seconds, float angle) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; var f0 = pc.transform.forward; f0.y = 0f; f0.Normalize(); if (dist > 0f) { d.Stop(true); d.Set_Warp(pc.Get_position() + f0 * dist); } var sb = Buf(); sb.Length = 0; float t0 = Time.time; bool dodged = false; string last = ""; int sf0 = WL.Combat.Core.CombatEvents.SkillFired.Raised; UnityEngine.Events.UnityAction act = null; act = () => { if (pc == null || pc.m_animation == null) { Application.onBeforeRender -= act; return; } float t = Time.time - t0; if (!dodged && t >= delay) { dodged = true; var fw = pc.transform.forward; fw.y = 0f; pc.transform.rotation = Quaternion.LookRotation(Quaternion.Euler(0f, angle, 0f) * fw); pc.Play_Dodge(); sb.AppendFormat("{0:F2} ▶ 구르기 → {1}", t, pc.Get_CurAnim()).Append('\n'); } string key = Clip(pc.m_animation) + " " + pc.Get_CurAnim(); if (key != last) { last = key; sb.AppendFormat("{0:F2} {1} · 스킬발사 +{2}", t, key, WL.Combat.Core.CombatEvents.SkillFired.Raised - sf0).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; sb.Append("끝").Append('\n'); } }; Application.onBeforeRender += act; return "발동: " + WL.Combat.Weapon.WLSkillHotkey.Fire(slot); } /// 점프 스킬 먼지(Effect_PCDash) — 켜진 것의 앞 방향과 PC → 대상 방향의 내적(−1 = 정반대) · 크기. public static object Dust() { var pc = MyValue.MyPC as PCActor; var d = Dummy(); var sb = new StringBuilder(); Vector3 to = d != null && pc != null ? d.Get_position() - pc.Get_position() : Vector3.forward; to.y = 0f; to.Normalize(); foreach (var tf in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { if (!tf.name.StartsWith("Effect_PCDash") || (tf.parent != null && tf.parent.name.StartsWith("Effect_PCDash"))) continue; var fw = tf.forward; fw.y = 0f; fw.Normalize(); sb.Append(tf.name).Append(" 앞·대상방향 ").Append(Vector3.Dot(fw, to).ToString("F2")).Append(" 크기 ").Append(tf.localScale.x.ToString("F3")).Append(" | "); } return sb.Length > 0 ? sb.ToString() : "켜진 먼지 없음"; } /// 벽 튕김 시험 — 시험 몹을 가장 가까운 NavMesh 경계 inside m 안쪽에 두고 PC 를 그 뒤에 세워 경계 쪽으로 Fling(dist m · sec 초 · 튕김) · 몹 위치를 기록. public static object BounceTest(float inside, float dist, float sec) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; UnityEngine.AI.NavMeshHit e; if (!UnityEngine.AI.NavMesh.FindClosestEdge(d.Get_position(), out e, UnityEngine.AI.NavMesh.AllAreas)) return "경계 못 찾음"; var n = e.normal; n.y = 0f; n.Normalize(); var at = e.position + n * inside; d.Stop(true); d.Set_Warp(at); pc.Set_Warp(at + n * 1.2f); var sb = Buf(); sb.Length = 0; int b0 = WL.Combat.Weapon.WLLaunch.Bounces; float t0 = Time.time; Vector3 p0 = d.Get_position(); sb.Append("경계 ").Append(e.position.ToString("F2")).Append(" 법선 ").Append(n.ToString("F2")).Append(" · 몹 경계까지 ").Append(Vector3.Dot(d.Get_position() - e.position, n).ToString("F2")).Append('\n'); WL.Combat.Weapon.WLLaunch.Fling(d, pc, dist, sec, 0.5f, 0.5f, true); UnityEngine.Events.UnityAction act = null; act = () => { if (d == null) { Application.onBeforeRender -= act; return; } float t = Time.time - t0; sb.AppendFormat("{0:F2} 경계까지 {1:F2} · 이동 {2:F2} · 튕김 +{3}", t, Vector3.Dot(d.Get_position() - e.position, n), Vector3.Distance(p0, d.Get_position()), WL.Combat.Weapon.WLLaunch.Bounces - b0).Append('\n'); if (t > sec + 0.1f) { Application.onBeforeRender -= act; d.Stop(false); } }; Application.onBeforeRender += act; return "튕김 시험 시작"; } /// 시험 몹을 dist 에 세우고 주력 스킬(Q)을 쏜 뒤 2~4 프레임째에 켜진 먼지(Effect_PCDash)의 방향 · 크기를 기록. public static object JumpDust(float dist) { var pc = MyValue.MyPC as PCActor; var d = Dummy(); if (pc == null || d == null) return "PC/몹 없음"; var f0 = pc.transform.forward; f0.y = 0f; f0.Normalize(); d.Stop(true); d.Set_Warp(pc.Get_position() + f0 * dist); var sb = Buf(); sb.Length = 0; int frames = 0; Vector3 to = d.Get_position() - pc.Get_position(); to.y = 0f; to.Normalize(); UnityEngine.Events.UnityAction act = null; act = () => { frames++; if (frames >= 2 && frames <= 4) { foreach (var tf in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { if (!tf.name.StartsWith("Effect_PCDash") || (tf.parent != null && tf.parent.name.StartsWith("Effect_PCDash"))) continue; var fw = tf.forward; fw.y = 0f; fw.Normalize(); sb.AppendFormat("f{0} {1} 앞·점프방향 {2:F2} · 크기 {3:F3}", frames, tf.name, Vector3.Dot(fw, to), tf.localScale.x).Append('\n'); } } if (frames > 4) Application.onBeforeRender -= act; }; Application.onBeforeRender += act; return "발동: " + WL.Combat.Weapon.WLSkillHotkey.Fire(1); } }