Project_WL/AgentScripts/WL907_Probe.cs

182 lines
11 KiB
C#

// WL907_Probe.cs — #907 Play 중 섬 몹 라인업 확인(몹 ID · 프리팹 · 크기 · 실제 높이 · 애니메이터 상태 · PC 와 거리) — 읽기 전용
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
public static class WL907_Probe
{
// 실제 정점(BakeMesh) 기준 — 렌더러 bounds 는 여유가 커서 과대 측정
static float Height(GameObject go, out float width)
{
var mesh = new Mesh(); float top = float.MinValue; var lo = new Vector2(float.MaxValue, float.MaxValue); var hi = new Vector2(float.MinValue, float.MinValue);
foreach (var r in go.GetComponentsInChildren<SkinnedMeshRenderer>(false))
{
r.BakeMesh(mesh, true); var tr = r.transform;
foreach (var v in mesh.vertices)
{
var p = tr.position + tr.rotation * v;
top = Mathf.Max(top, p.y); lo = Vector2.Min(lo, new Vector2(p.x, p.z)); hi = Vector2.Max(hi, new Vector2(p.x, p.z));
}
}
Object.Destroy(mesh);
width = top > float.MinValue ? Mathf.Max(hi.x - lo.x, hi.y - lo.y) : 0f;
return top > float.MinValue ? top - go.transform.position.y : 0f;
}
/// <summary>꺼져 있는 섬 존 몹 중 id 인 것을 깨운다(On_Regen) · 그 밖의 켜진 몹(시험 몹 제외)은 끈다.</summary>
public static object Wake(int id, bool offOthers)
{
var f = typeof(MobActor).GetField("m_MonsterTableData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var d0 = WL.IslandMob.WLTrailTest.Dummy; int off = 0; string woke = "없음"; var ids = new StringBuilder();
foreach (var m in Object.FindObjectsByType<MobActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
{
var d = f.GetValue(m) as MonsterTableData; int mid = d != null ? d.n_MonsterID : 0;
ids.Append(mid).Append(m.gameObject.activeSelf ? "+ " : "- ");
if (offOthers && m.gameObject.activeSelf && !ReferenceEquals(m, d0) && mid != id) { m.Off(); off++; }
}
foreach (var m in Object.FindObjectsByType<MobActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
{
var d = f.GetValue(m) as MonsterTableData;
if (d == null || d.n_MonsterID != id || m.gameObject.activeSelf) continue;
m.On_Regen(); woke = m.name + " 켜짐 " + m.gameObject.activeSelf; break;
}
return "몹 목록(+켜짐 -꺼짐) " + ids + " · 끈 몹 " + off + " · " + woke;
}
public static object Mobs()
{
var sb = new StringBuilder();
sb.AppendLine("lineup applied " + WL.Mobs.WLMobLineup.Applied + " · " + WL.Mobs.WLMobLineup.LastLog);
var pc = Object.FindObjectsByType<PCActor>(FindObjectsSortMode.None).FirstOrDefault(p => p.IsMainPC());
if (pc != null) { float pw; sb.AppendLine("PC " + pc.name + " 높이 " + Height(pc.gameObject, out pw).ToString("F2") + " m · 폭 " + pw.ToString("F2")); }
var f = typeof(MobActor).GetField("m_MonsterTableData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var m in Object.FindObjectsByType<MobActor>(FindObjectsSortMode.None))
{
if (!m.gameObject.activeInHierarchy) continue;
var d = f != null ? f.GetValue(m) as MonsterTableData : null;
var an = m.GetComponent<Animator>();
string st = "-";
if (an != null && an.runtimeAnimatorController != null)
foreach (var n in new[] { "run", "idle", "attack", "hit", "die", "stun", "casting", "casting_end" })
if (an.GetCurrentAnimatorStateInfo(0).IsName(n)) { st = n; break; }
float w; float h = Height(m.gameObject, out w);
sb.AppendLine((d != null ? d.n_MonsterID.ToString() : "?") + " " + m.name + " · " + (d != null ? d.s_MonsterPrefab : "-") + " · 크기 " + m.transform.localScale.x.ToString("F2")
+ " · 높이 " + h.ToString("F2") + " m · 폭 " + w.ToString("F2") + " · 컨트롤러 " + (an != null && an.runtimeAnimatorController ? an.runtimeAnimatorController.name : "-") + " · 상태 " + st
+ " · 표크기 " + (d != null ? d.f_DefaultScale.ToString("F2") : "-") + " · ObjSize " + ((Vector3)typeof(Actor).GetField("ObjSize", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(m)).ToString("F2") + " · 중심 y " + (m.Get_Center_position().y - m.transform.position.y).ToString("F2") + " · 부품 " + string.Join("/", m.GetComponents<MonoBehaviour>().Where(c => c != null).Select(c => c.GetType().Name)) + " · 죽음 " + m.IsDead() + (pc != null ? " · PC 거리 " + Vector3.Distance(pc.Get_position(), m.Get_position()).ToString("F2") : ""));
}
return sb.ToString();
}
static MobActor Pick(int id)
{
var f = typeof(MobActor).GetField("m_MonsterTableData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MobActor pick = null;
foreach (var m in Object.FindObjectsByType<MobActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
{
var d = f.GetValue(m) as MonsterTableData;
if (d == null || d.n_MonsterID != id) continue;
if (m.isActiveAndEnabled && !m.IsDead()) return m;
if (pick == null) pick = m;
}
if (pick != null && !pick.isActiveAndEnabled) { int prev = WL.IslandMob.WLTrailTest.Override; WL.IslandMob.WLTrailTest.Override = 0; try { pick.On_Regen(); } finally { WL.IslandMob.WLTrailTest.Override = prev; } }
return pick;
}
/// <summary>101~104 를 PC 앞(카메라 쪽 dist m)에 gap m 간격으로 한 줄로 세우고 멈춘다 — 크기 · 모습 비교 캡처용(실행 중만).</summary>
public static object Lineup(float dist, float gap) { return LineupIds("101,102,103,104", dist, gap); }
public static object LineupIds(string ids, float dist, float gap)
{
var pc = MyValue.MyPC; var cam = Camera.main;
if (pc == null || cam == null) return "PC/카메라 없음";
var fw = cam.transform.forward; fw.y = 0f; fw.Normalize(); var rt = cam.transform.right; rt.y = 0f; rt.Normalize();
var sb = new StringBuilder(); int i = 0;
foreach (var id in ids.Split(',').Select(int.Parse))
{
var m = Pick(id);
if (m == null) { sb.Append(id).Append(" 없음 · "); i++; continue; }
var want = pc.Get_position() - fw * dist + rt * ((i - 1.5f) * gap);
UnityEngine.AI.NavMeshHit hit;
if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 2f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position;
m.Stop(true); m.Set_Warp(want);
m.transform.rotation = Quaternion.LookRotation(-fw);
sb.Append(id).Append(' ').Append(m.name).Append(" 켜짐 ").Append(m.isActiveAndEnabled).Append(" · ");
i++;
}
return sb.ToString();
}
/// <summary>id 몹만 다시 움직이게 한다(나머지는 멈춘 채) · PC 피격 이벤트 수(기준점)를 돌려준다.</summary>
public static object Release(int id)
{
var m = Pick(id); if (m == null) return "없음";
m.Stop(false);
return m.name + " 풀림 · PC 피격 이벤트 " + WL.Combat.Core.CombatEvents.Damaged.Raised;
}
public static object UnfreezeAll()
{
int n = 0;
foreach (var m in Object.FindObjectsByType<MobActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { m.Stop(false); n++; }
return "풀림 " + n + " · PC 피격 이벤트 " + WL.Combat.Core.CombatEvents.Damaged.Raised;
}
public static object Counters()
{
var pc = MyValue.MyPC;
return "판정 생성 " + WL.Combat.Core.CombatEvents.HitboxSpawned.Raised + " · PC 피격 " + WL.Combat.Core.CombatEvents.Damaged.Raised + " · PC HP " + (pc != null ? pc.Get_HP().ToString("0.#") : "-") + " · t " + Time.time.ToString("0.0");
}
// ── 몹 판정 생성 기록(HitboxSpawned · 쏜 몹 · 프리팹 · 시작점 높이 · PC 와 거리) + PC 피격 기록 ──
// run_script 는 호출마다 새 어셈블리 — 정적 필드가 공유되지 않아 AppDomain 데이터에 둔다
static StringBuilder s_shots { get { var b = System.AppDomain.CurrentDomain.GetData("wl907_shots") as StringBuilder; if (b == null) { b = new StringBuilder(); System.AppDomain.CurrentDomain.SetData("wl907_shots", b); } return b; } }
static float s_until;
static void OnShot(in WL.Combat.Core.HitboxSpawnedEvent e)
{
if (Time.time > s_until) return;
if (!(e.shooter is MobActor)) return;
var pc = MyValue.MyPC;
s_shots.AppendFormat("{0:F2} 쏨 {1} {2} 시작 y{3:F2} · PC 거리 {4:F2}", Time.time, e.shooter.name, e.prefab, e.startPos.y - e.shooter.transform.position.y, pc != null ? Vector3.Distance(pc.Get_position(), e.startPos) : -1f).AppendLine();
}
static void OnDmg(in WL.Combat.Core.DamagedEvent e)
{
if (Time.time > s_until) return;
s_shots.AppendFormat("{0:F2} PC 피격 ← {1} 피해 {2:0.#} 무적 {3}", Time.time, e.attacker != null ? e.attacker.name : "-", e.damage, e.invincible).AppendLine();
}
public static object WatchShots(float seconds)
{
s_shots.Length = 0; s_until = Time.time + seconds;
WL.Combat.Core.CombatEvents.HitboxSpawned.Remove(OnShot); WL.Combat.Core.CombatEvents.HitboxSpawned.Add(OnShot);
WL.Combat.Core.CombatEvents.Damaged.Remove(OnDmg); WL.Combat.Core.CombatEvents.Damaged.Add(OnDmg);
return "기록 시작 " + seconds + " s";
}
public static object ShotLog() { return s_shots.ToString(); }
/// <summary>id 몹(시험 몹 아닌 것)을 깨워 PC 에서 dist m(카메라 쪽)에 두고 PC 를 표적으로 잡게 한 뒤(비선공도 반격 상태) 판정 · 피격 기록을 seconds 초 시작.</summary>
public static object AttackTest(int id, float dist, float seconds)
{
var pc = MyValue.MyPC; var cam = Camera.main; var dummy = WL.IslandMob.WLTrailTest.Dummy;
if (pc == null || cam == null) return "PC/카메라 없음";
var f = typeof(MobActor).GetField("m_MonsterTableData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MobActor m = null;
foreach (var x in Object.FindObjectsByType<MobActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
{
var d = f.GetValue(x) as MonsterTableData;
if (d == null || d.n_MonsterID != id || ReferenceEquals(x, dummy)) continue;
if (x.isActiveAndEnabled && !x.IsDead()) { m = x; break; }
if (m == null && !x.gameObject.activeSelf) m = x;
}
if (m == null) return id + " 없음";
if (!m.isActiveAndEnabled) { int prev = WL.IslandMob.WLTrailTest.Override; WL.IslandMob.WLTrailTest.Override = 0; try { m.On_Regen(); } finally { WL.IslandMob.WLTrailTest.Override = prev; } }
var fw = cam.transform.forward; fw.y = 0f; fw.Normalize();
var want = pc.Get_position() - fw * dist;
UnityEngine.AI.NavMeshHit hit;
if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 2f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position;
m.Stop(false); m.Set_Warp(want);
m.Change_Target(true, pc, true);
WatchShots(seconds);
return m.name + " 켜짐 " + m.isActiveAndEnabled + " · PC 거리 " + Vector3.Distance(pc.Get_position(), m.Get_position()).ToString("F2");
}
}