320 lines
19 KiB
C#
320 lines
19 KiB
C#
// 발주서 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);
|
||
}
|
||
}
|