Project_WL/AgentScripts/WL813t3_Probe.cs

320 lines
19 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.

// 발주서 WL-813t3 프로브 — 에디트 모드 · Play 0 · 로그인 0
//
// unity command --project-path E:\NerdNavis\WL_wt\data run_script --file AgentScripts/WL813t3_Probe.cs --entry WL813t3_Probe.Run --timeout 900
//
// ① 시야 3 m (가짜 적 2.5 / 3.5 m → 유지 / 놓음 · 락 · 이동 정지 경로)
// ② 보스 평지 스폰 (ResolveBossPin 실호출 · 아레나 앵커 · y 컷)
// ③ C8 진리표 (값 원복 = 813t2 동작) · GC
//
// 🔴 using 금지(CLAUDE.md §1) — 네임스페이스를 풀어 쓴다.
public static class WL813t3_Probe
{
const string kOut = "AgentScripts/staging/WL813t3/PROBE.txt";
static System.Text.StringBuilder sb;
static int s_fail;
static void L(string m) { sb.AppendLine(m); }
static string P(bool ok) { if (!ok) s_fail++; return ok ? "[PASS]" : "[FAIL]"; }
static string V(UnityEngine.Vector3 v) { return v.ToString("F2"); }
static UnityEngine.GameObject s_pcGo, s_nearGo, s_farGo, s_lockGo;
static PCActor s_pc;
static Actor s_near, s_far, s_lock;
static PCActor s_prevPc;
public static string Run()
{
sb = new System.Text.StringBuilder();
s_fail = 0;
L("===== WL-813t3 프로브 · " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " =====");
L("에디트 모드 · Play 0 · 로그인 0 · 실 에셋은 읽기만 한다.");
try
{
Setup();
Sec1_Decide();
Sec2_Live();
Sec3_BossPin();
Sec4_C8();
Sec5_GC();
}
catch (System.Exception e) { L("[에러] " + e.GetType().Name + " — " + e.Message); L(e.StackTrace); s_fail++; }
finally { Teardown(); }
L("");
L("RESULT " + (s_fail == 0 ? "PASS" : "FAIL(" + s_fail + ")"));
try
{
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "AgentScripts/staging/WL813t3"));
System.IO.File.WriteAllText(System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), kOut.Replace('/', System.IO.Path.DirectorySeparatorChar)),
sb.ToString(), new System.Text.UTF8Encoding(true));
}
catch (System.Exception e) { UnityEngine.Debug.LogWarning("[WL813t3] 프로브 저장 실패: " + e.Message); }
return sb.ToString();
}
// ─────────────────────────────────────────────────────────────────────────
static Actor MakeActor(string name, eRole role, eSubRol sub, UnityEngine.Vector3 pos, out UnityEngine.GameObject go)
{
go = new UnityEngine.GameObject("__WL813t3_" + name);
go.transform.position = pos;
var a = go.AddComponent<Actor>();
a.m_Role = role; a.m_SubRole = sub;
return a;
}
static void Setup()
{
s_prevPc = MyValue.MyPC;
s_pcGo = new UnityEngine.GameObject("__WL813t3_PC");
s_pcGo.transform.position = UnityEngine.Vector3.zero;
s_pc = s_pcGo.AddComponent<PCActor>();
s_pc.m_Role = eRole.PC; s_pc.m_SubRole = eSubRol.None;
MyValue.MyPC = s_pc;
s_near = MakeActor("near", eRole.Mob, eSubRol.None, new UnityEngine.Vector3(2.5f, 0f, 0f), out s_nearGo);
s_far = MakeActor("far", eRole.Mob, eSubRol.None, new UnityEngine.Vector3(3.5f, 0f, 0f), out s_farGo);
s_lock = MakeActor("lockedBoss", eRole.Mob, eSubRol.Boss, new UnityEngine.Vector3(9f, 0f, 0f), out s_lockGo);
s_lock.m_IsLockTarget = true;
WL.Settings.WLTargetingSettings.ClearCache();
WL.Combat.Auto.SightRangeGuard.RuntimeDisabled = false;
WL.Combat.Auto.SightRangeGuard.ResetDiagnostics();
L("");
L("── ⓪ 준비");
L(" " + P(s_pc.IsMainPC()) + " 가짜 메인 PC · MyValue.MyPC 등록");
var t = WL.Settings.WLTargetingSettings.Instance;
L(" " + P(t != null) + " WLTargetingSettings 에셋 로드");
if (t != null)
L(" 실값 retargetSearchRange=" + t.retargetSearchRange + " · chaseRange=" + t.chaseRange
+ " · chaseDropIgnoresLock=" + t.chaseDropIgnoresLock + " · stopChaseMoveOnDrop=" + t.stopChaseMoveOnDrop
+ " · chaseCheckSeconds=" + t.chaseCheckSeconds);
L(" " + P(WL.Combat.Auto.SightRangeGuard.Active) + " SightRangeGuard.Active (chaseRange > 0)");
L(" PC Agent = " + (s_pc.Get_Agent() == null ? "없음(에디트 모드 — 이동 정지 경로는 미확인)" : "있음"));
}
static void Teardown()
{
try
{
WL.Settings.WLTargetingSettings.SetInstanceForProbe(null);
WL.Settings.WLTargetingSettings.ClearCache();
WL.Combat.Nav.WLTerrainGuardSettings.ClearCache();
WL.Combat.Auto.SightRangeGuard.RuntimeDisabled = false;
WL.Combat.Auto.SightRangeGuard.ResetDiagnostics();
WL.Combat.Nav.SpawnPositionGuard.ResetDiagnostics();
MyValue.MyPC = s_prevPc;
Kill(s_pcGo); Kill(s_nearGo); Kill(s_farGo); Kill(s_lockGo);
}
catch { }
}
static void Kill(UnityEngine.GameObject go) { if (go != null) UnityEngine.Object.DestroyImmediate(go); }
// ─────────────────────────────────────────────────────────────────────────
// ① 순수 판정 (Decide)
// ─────────────────────────────────────────────────────────────────────────
static void Sec1_Decide()
{
L("");
L("── ① 시야 3 m 판정표 (SightRangeGuard.Decide · 순수 함수)");
L(" | 거리 | 락 | ignoreLock | 판정 |");
Row(3f, 0.0f, false, false, WL.Combat.Auto.SightRangeGuard.KeepTarget, "0.0 m(붙어 있음)");
Row(3f, 2.5f, false, false, WL.Combat.Auto.SightRangeGuard.KeepTarget, "2.5 m");
Row(3f, 3.0f, false, false, WL.Combat.Auto.SightRangeGuard.KeepTarget, "3.0 m(경계 = 유지)");
Row(3f, 3.01f, false, false, WL.Combat.Auto.SightRangeGuard.DropTarget, "3.01 m");
Row(3f, 3.5f, false, false, WL.Combat.Auto.SightRangeGuard.DropTarget, "3.5 m");
Row(3f, 14f, false, false, WL.Combat.Auto.SightRangeGuard.DropTarget, "14 m(813w 보스 홀드 반경)");
Row(3f, 3.5f, true, false, WL.Combat.Auto.SightRangeGuard.KeepByLock, "3.5 m · 락 · 락 존중");
Row(3f, 3.5f, true, true, WL.Combat.Auto.SightRangeGuard.DropTarget, "3.5 m · 락 · 락 무시");
Row(0f, 100f, false, false, WL.Combat.Auto.SightRangeGuard.KeepDisabled, "chaseRange 0 = 축 off(C8)");
}
static void Row(float range, float dist, bool locked, bool ignore, int expect, string label)
{
int got = WL.Combat.Auto.SightRangeGuard.Decide(range, dist, locked, ignore);
L(" " + P(got == expect) + " " + label + " → " + Name(got) + " (기대 " + Name(expect) + ")");
}
static string Name(int v)
{
if (v == WL.Combat.Auto.SightRangeGuard.KeepTarget) return "유지";
if (v == WL.Combat.Auto.SightRangeGuard.DropTarget) return "놓음";
if (v == WL.Combat.Auto.SightRangeGuard.KeepByLock) return "락유지";
return "축off";
}
// ─────────────────────────────────────────────────────────────────────────
// ② 실제 액터로 CheckOnce
// ─────────────────────────────────────────────────────────────────────────
static void Sec2_Live()
{
L("");
L("── ② 가짜 적으로 실호출 (SightRangeGuard.CheckOnce · 원본 Del_Target 경로)");
WL.Combat.Auto.SightRangeGuard.ResetDiagnostics();
s_pc.Set_Target(s_near);
bool dropped = WL.Combat.Auto.SightRangeGuard.CheckOnce();
L(" " + P(!dropped && s_pc.isTarget && object.ReferenceEquals(s_pc.Get_Target(), s_near))
+ " 2.5 m 적 → 타깃 유지 (거리 실측 " + WL.Combat.Auto.SightRangeGuard.LastDistance.ToString("F2") + " m)");
s_pc.Set_Target(s_far);
dropped = WL.Combat.Auto.SightRangeGuard.CheckOnce();
L(" " + P(dropped && !s_pc.isTarget)
+ " 3.5 m 적 → **타깃 해제** (거리 실측 " + WL.Combat.Auto.SightRangeGuard.LastDistance.ToString("F2")
+ " m · Dropped=" + WL.Combat.Auto.SightRangeGuard.Dropped + " · 마지막 " + WL.Combat.Auto.SightRangeGuard.LastDroppedName + ")");
// 락 타깃(813w 보스) — 기본값(락 존중)에서는 유지된다
s_pc.Set_Target(s_lock);
dropped = WL.Combat.Auto.SightRangeGuard.CheckOnce();
bool lockKept = !dropped && s_pc.isTarget;
L(" " + P(lockKept) + " 9 m 락 보스 → 유지(원본 락 존중 · LockKept=" + WL.Combat.Auto.SightRangeGuard.LockKept + ")"
+ " · SO `bossLockTarget` 을 0 으로 두면 813w 가 애초에 락을 걸지 않는다");
// 락 무시 스위치
var tmp = MakeTemp();
tmp.chaseRange = 3f; tmp.chaseDropIgnoresLock = true; tmp.stopChaseMoveOnDrop = false;
WL.Settings.WLTargetingSettings.SetInstanceForProbe(tmp);
s_pc.Set_Target(s_lock);
dropped = WL.Combat.Auto.SightRangeGuard.CheckOnce();
L(" " + P(dropped && !s_pc.isTarget) + " chaseDropIgnoresLock=1 → 락 보스도 해제");
WL.Settings.WLTargetingSettings.SetInstanceForProbe(null);
UnityEngine.Object.DestroyImmediate(tmp);
s_pc.Del_Target(true);
L(" 이동 정지 경로(MoveStopped=" + WL.Combat.Auto.SightRangeGuard.MoveStopped
+ " · MoveSkipped=" + WL.Combat.Auto.SightRangeGuard.MoveSkipped
+ ") — 🔴 에디트 모드엔 NavMeshAgent·애니메이터가 없어 **실이동 정지는 미확인**(§QA 로 확인).");
}
static WL.Settings.WLTargetingSettings MakeTemp()
{
var so = UnityEngine.ScriptableObject.CreateInstance<WL.Settings.WLTargetingSettings>();
var real = UnityEngine.Resources.Load<WL.Settings.WLTargetingSettings>("WL/WLTargetingSettings");
if (real != null)
{
so.frontConeDeg = real.frontConeDeg; so.facingWeight = real.facingWeight;
so.attackStartRetarget = real.attackStartRetarget; so.retargetOnKill = real.retargetOnKill;
so.immediateAttackMargin = real.immediateAttackMargin; so.retargetSearchRange = real.retargetSearchRange;
so.pcAggroOnHit = real.pcAggroOnHit; so.autoEngage = real.autoEngage;
so.chaseRange = real.chaseRange; so.chaseDropIgnoresLock = real.chaseDropIgnoresLock;
so.stopChaseMoveOnDrop = real.stopChaseMoveOnDrop; so.chaseCheckSeconds = real.chaseCheckSeconds;
}
return so;
}
// ─────────────────────────────────────────────────────────────────────────
// ③ 보스 평지 스폰
// ─────────────────────────────────────────────────────────────────────────
static void Sec3_BossPin()
{
L("");
L("── ③ 보스 평지 스폰 (SpawnPositionGuard.ResolveBossPin · 순수 함수)");
var cfg = WL.Combat.Nav.WLTerrainGuardSettings.Instance;
L(" " + P(cfg != null) + " WLTerrainGuardSettings 에셋 로드");
if (cfg == null) return;
L(" 실값 bossSpawnPinEnabled=" + cfg.bossSpawnPinEnabled + " · bossSpawnPinRadius=" + cfg.bossSpawnPinRadius
+ " · bossSpawnPinTolerance=" + cfg.bossSpawnPinTolerance + " · maxWalkableY=" + cfg.maxWalkableY
+ " · dwellSeconds=" + cfg.dwellSeconds);
var arena = WL.Combat.Boss.BossArena.Center;
L(" BossArena.Center(에디트 모드 · 보류 목록 비어 있음 → SO arenaCenter) = " + V(arena));
bool navUp = UnityEngine.AI.NavMesh.CalculateTriangulation().vertices.Length > 0;
L(" 씬에 NavMesh 올라와 있음 = " + navUp + (navUp ? "" : " → 아래 표집 결과는 「NavMesh 없음」이 정상(측정은 WL813t3_Measure 가 한다)"));
UnityEngine.Vector3 outp;
int r1 = WL.Combat.Nav.SpawnPositionGuard.ResolveBossPin(cfg, new UnityEngine.Vector3(139f, 6f, -20f), out outp);
L(" 언덕 위 가짜 보스 (139, 6.00, -20) → " + PinName(r1) + " " + V(outp));
int r2 = WL.Combat.Nav.SpawnPositionGuard.ResolveBossPin(cfg, arena, out outp);
L(" 아레나 제자리 " + V(arena) + " → " + PinName(r2) + " " + V(outp));
int r3 = WL.Combat.Nav.SpawnPositionGuard.ResolveBossPin(cfg, new UnityEngine.Vector3(128f, 8f, -58f), out outp);
L(" D-48 언덕 (128, 8.00, -58) → " + PinName(r3) + " " + V(outp));
var off = UnityEngine.ScriptableObject.CreateInstance<WL.Combat.Nav.WLTerrainGuardSettings>();
off.bossSpawnPinEnabled = false;
int r4 = WL.Combat.Nav.SpawnPositionGuard.ResolveBossPin(off, new UnityEngine.Vector3(139f, 6f, -20f), out outp);
L(" " + P(r4 == WL.Combat.Nav.SpawnPositionGuard.BossPinNoAnchor) + " C8: bossSpawnPinEnabled=0 → 손대지 않음(" + PinName(r4) + ")");
UnityEngine.Object.DestroyImmediate(off);
}
static string PinName(int v)
{
if (v == WL.Combat.Nav.SpawnPositionGuard.BossPinNoAnchor) return "앵커없음(무개입)";
if (v == WL.Combat.Nav.SpawnPositionGuard.BossPinAlready) return "이미 제자리";
if (v == WL.Combat.Nav.SpawnPositionGuard.BossPinMoved) return "**옮김**";
return "앵커 부적격";
}
// ─────────────────────────────────────────────────────────────────────────
// ④ C8 진리표
// ─────────────────────────────────────────────────────────────────────────
static void Sec4_C8()
{
L("");
L("── ④ C8 진리표 (값 원복 = 813t2 동작)");
var t0 = MakeTemp();
t0.chaseRange = 0f;
WL.Settings.WLTargetingSettings.SetInstanceForProbe(t0);
bool active0 = WL.Combat.Auto.SightRangeGuard.Active;
s_pc.Set_Target(s_far);
bool drop0 = WL.Combat.Auto.SightRangeGuard.CheckOnce();
L(" " + P(!active0 && !drop0 && s_pc.isTarget) + " chaseRange=0 → Active=false · 3.5 m 적 유지(= 813t3 이전 동작 100%)");
s_pc.Del_Target(true);
WL.Settings.WLTargetingSettings.SetInstanceForProbe(null);
UnityEngine.Object.DestroyImmediate(t0);
WL.Combat.Auto.SightRangeGuard.RuntimeDisabled = true;
bool active1 = WL.Combat.Auto.SightRangeGuard.Active;
WL.Combat.Auto.SightRangeGuard.RuntimeDisabled = false;
L(" " + P(!active1) + " RuntimeDisabled=true → Active=false");
var t1 = MakeTemp(); t1.retargetSearchRange = 0f;
WL.Settings.WLTargetingSettings.SetInstanceForProbe(t1);
L(" " + P(WL.Settings.WLTargetingSettings.RetargetSearchRange == 0f)
+ " retargetSearchRange=0 → MyActor.Get_WLFindRange() 가 원본 f_FindEnemyRange(table 6 m + AttackRange_Up)로 되돌아간다");
WL.Settings.WLTargetingSettings.SetInstanceForProbe(null);
UnityEngine.Object.DestroyImmediate(t1);
var g0 = UnityEngine.ScriptableObject.CreateInstance<WL.Combat.Nav.WLTerrainGuardSettings>();
g0.enabled = false;
WL.Combat.Nav.WLTerrainGuardSettings.SetInstanceForProbe(g0);
L(" " + P(!WL.Combat.Nav.WLTerrainGuardSettings.Active && !WL.Combat.Nav.SpawnPositionGuard.Active)
+ " WLTerrainGuardSettings.enabled=0 → 813t2 러너·스폰 보정 전부 off(보스 고정 포함)");
WL.Combat.Nav.WLTerrainGuardSettings.SetInstanceForProbe(null);
WL.Combat.Nav.WLTerrainGuardSettings.ClearCache();
UnityEngine.Object.DestroyImmediate(g0);
L(" " + P(WL.Combat.Nav.WLTerrainGuardSettings.Instance != null) + " 실 에셋 복구 확인(프로브가 실 에셋을 쓰지 않았다)");
var b = UnityEngine.Resources.Load<WL.Combat.Boss.WLBossArenaSettings>("WL/WLBossArenaSettings");
L(" " + P(b != null && !b.useFieldBossPositionAsCenter)
+ " WLBossArenaSettings.useFieldBossPositionAsCenter=0 → 아레나 중심이 **SO 값**(139, 0.8, -20)으로 고정 (1 로 되돌리면 스포너 노드 위치)");
var rg = UnityEngine.Resources.Load<WL.Combat.Run.WLRunGuideSettings>("WL/WLRunGuideSettings");
L(" " + P(rg != null && !rg.bossLockTarget) + " WLRunGuideSettings.bossLockTarget=0 → 813w 보스 락타깃 off(1 로 되돌리면 원상)");
}
// ─────────────────────────────────────────────────────────────────────────
// ⑤ GC
// ─────────────────────────────────────────────────────────────────────────
static void Sec5_GC()
{
L("");
L("── ⑤ GC (틱 · 판정 반복)");
s_pc.Set_Target(s_near); // 유지 경로(로그·문자열 없음)로 돈다
WL.Combat.Auto.SightRangeGuard.ResetDiagnostics();
for (int i = 0; i < 50; i++) WL.Combat.Auto.SightRangeGuard.CheckOnce();
System.GC.Collect();
long a = System.GC.GetTotalMemory(false);
for (int i = 0; i < 1000; i++) WL.Combat.Auto.SightRangeGuard.CheckOnce();
var cfg = WL.Combat.Nav.WLTerrainGuardSettings.Instance;
UnityEngine.Vector3 o;
for (int i = 0; i < 1000; i++) WL.Combat.Nav.SpawnPositionGuard.ResolveBossPin(cfg, new UnityEngine.Vector3(139f, 6f, -20f), out o);
for (int i = 0; i < 1000; i++) WL.Combat.Auto.SightRangeGuard.Decide(3f, 3.5f, false, false);
long b = System.GC.GetTotalMemory(false);
L(" " + P(b - a <= 0) + " CheckOnce ×1000 + ResolveBossPin ×1000 + Decide ×1000 → Δ " + (b - a) + " B");
s_pc.Del_Target(true);
}
}