Project_WL/AgentScripts/WL813q3_Probe.cs

385 lines
24 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-813q3 프로브 — 결과창 「보스 처치 : 미처치 (HP 남음 N %)」 가 813w6 `RunEnded` 페이로드
// `bossHpRatio` 를 쓰는지 실측(에디트 모드 · Play 0 · 로그인 0 · #813).
// unity command run_script --file AgentScripts/WL813q3_Probe.cs --entry WL813q3_Probe.RunAll
// 산출물: <worktree>/AgentScripts/WL813q3_PROBE.txt
//
// 🔴 에셋 저장 0 — WLRunUiSettings 값은 **메모리에서만** 바꾸고 finally 에서 원복한다.
// AssetDatabase.SaveAssets / EditorUtility.SetDirty 호출 **0**. 파일 크기·수정시각을 전후로 찍어 증거로 남긴다.
// 🔴 Gameplay 는 읽기·공개 API 호출만(파일 수정 0). Play 진입 0.
// 🔴 핵심 계측 = **「늦게 그려도 값이 안 바뀐다」**.
// RunEnded 를 받은 뒤 BossArena 가 미뤄 둔 Park(만피 복구)을 실제로 돌리고, 보스 HP 바 미러까지
// 100 % 로 만든 다음 결과창을 **다시 그려서** 문구가 그대로 24 % 인지 본다.
// 스위치를 끄면(813q2 동작) 같은 자리에서 100 % 가 나온다 = D-44 재현.
using System;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEditor;
using WL.UI;
using WL.Combat.Run;
using WL.Combat.Boss;
public static class WL813q3_Probe
{
const string kAsset = "Assets/WL/UI/Settings/Resources/WL/WLRunUiSettings.asset";
static StringBuilder _o;
static int _pass, _fail, _note;
static void H(string s) { _o.AppendLine(); _o.AppendLine("── " + s); }
static void N(string s) { _o.AppendLine(" " + s); }
static void Note(string s) { _note++; _o.AppendLine(" [NOTE] " + s); }
static bool Chk(bool ok, string what)
{
if (ok) { _pass++; _o.AppendLine(" [PASS] " + what); }
else { _fail++; _o.AppendLine(" [FAIL] " + what); }
return ok;
}
static string Root { get { return Directory.GetParent(Application.dataPath).FullName; } }
static string OutPath(string n) { return Path.Combine(Path.Combine(Root, "AgentScripts"), n); }
/// <summary>본문에서 한 행의 값 문자열을 뽑는다(노드 없이도 되는 유일한 경로 = BodyText).</summary>
static string BodyRow(string label)
{
string body = RunResultUI.BodyText;
if (string.IsNullOrEmpty(body)) return "";
var lines = body.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
int k = lines[i].IndexOf(" : ", StringComparison.Ordinal);
if (k < 0) continue;
if (lines[i].Substring(0, k) == label) return lines[i].Substring(k + 3);
}
return "";
}
/// <summary>합성 RunResult — 배열 3종은 813p 계약대로 길이만 맞춘다.</summary>
static RunResult Fake(RunOutcome outcome, float bossClearSec, float ratio, double hp, double maxHp)
{
return new RunResult
{
outcome = outcome,
runIndex = 1,
totalSec = 241f,
runSeconds = 270f,
zoneClearSec = new float[] { 61.5f, -1f },
zoneKills = new int[] { 17, 12 },
zoneCount = 2,
zonesCleared = 1,
bossGateSec = 170f,
bossStartSec = 173f,
bossClearSec = bossClearSec,
bossHpRatio = ratio,
bossHp = hp,
bossHpMax = maxHp,
kills = 29,
bossKills = bossClearSec >= 0f ? 1 : 0,
maxChain = 7,
maxDamage = 4321d,
maxDamageBurst = 8888.5d,
levelFrom = 11,
levelTo = 12,
lootByGrade = new int[] { 0, 5, 2, 0, 0, 0, 0, 0, 0, 0 },
lootTotal = 7,
gold = 777,
deaths = 1,
potionsUsed = 2,
skillCasts = 40,
time = 0f,
frame = 0,
};
}
public static string RunAll()
{
_o = new StringBuilder(16000);
_o.AppendLine("== WL-813q3 프로브 — 결과창 보스 HP = RunEnded 페이로드 스냅샷 ==");
_o.AppendLine(" " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " · Unity " + Application.unityVersion +
" · isPlaying=" + Application.isPlaying + " · project=" + Root);
WLRunUiSettings.ClearCache();
var s = WLRunUiSettings.Instance;
if (s == null) { _o.AppendLine(" [FAIL] WLRunUiSettings 에셋 없음 — 중단"); return Flush(); }
string abs = Path.Combine(Root, kAsset.Replace('/', Path.DirectorySeparatorChar));
var fi0 = new FileInfo(abs); fi0.Refresh();
long len0 = fi0.Length; DateTime mt0 = fi0.LastWriteTimeUtc;
// ── 원복용 스냅샷 ────────────────────────────────────────────────────
bool o_payload = s.resultBossHpFromPayload, o_fromUi = s.resultBossHpFromUi;
bool o_cards = s.resultCountCardPicksInUi, o_elites = s.resultCountElitesInUi;
bool o_enabled = s.enabled_, o_result = s.resultEnabled;
var o_rows = s.resultRows;
float o_barRatio = BossHpBar.RunBossHpRatio;
bool o_subscribed = RunResultUI.Subscribed;
try
{
// ── ⓪ 설정·행 배열 실측 ─────────────────────────────────────────
H("⓪ 설정 실측 — 신규 값 2개 · 신규 행 1개");
N("에셋 = " + kAsset + " (" + len0 + " B · " + mt0.ToString("yyyy-MM-dd HH:mm:ss") + " UTC)");
N("resultBossHpFromPayload=" + s.resultBossHpFromPayload + " · resultBossHpFromUi=" + s.resultBossHpFromUi +
" · resultCountCardPicksInUi=" + s.resultCountCardPicksInUi + " · resultCountElitesInUi=" + s.resultCountElitesInUi);
N("문구 = \"" + s.resultBossNotClearedHpFormat + "\" / \"" + s.resultBossNotClearedText + "\" · 카드 행 이름 = \"" + s.rowCardPicksLabel + "\"");
Chk(s.resultBossHpFromPayload, "resultBossHpFromPayload 기본 on(813w6 페이로드 우선)");
Chk(s.resultCountCardPicksInUi, "resultCountCardPicksInUi 기본 on");
Chk(!string.IsNullOrEmpty(s.rowCardPicksLabel), "rowCardPicksLabel 값 있음");
var sbRows = new StringBuilder();
bool hasCard = false, hasElite = false, hasBoss = false;
for (int i = 0; i < s.resultRows.Length; i++)
{
if (i > 0) sbRows.Append(" · ");
sbRows.Append(s.resultRows[i]);
if (s.resultRows[i] == WLRunResultRow.CardPicks) hasCard = true;
if (s.resultRows[i] == WLRunResultRow.EliteKills) hasElite = true;
if (s.resultRows[i] == WLRunResultRow.BossClear) hasBoss = true;
}
N("resultRows(" + s.resultRows.Length + ") = " + sbRows);
Chk(hasBoss, "BossClear 행 있음");
Chk(hasElite, "EliteKills 행 있음(813q2)");
Chk(hasCard, "CardPicks 행 있음(813q3 신설)");
// 상자 안에 11행이 들어가는지 산술 실측(제목 + 행 + 버튼)
float top = s.resultPadTopPx + s.resultTitleFontPx + s.resultTitleGapPx;
float rowsH = s.resultRowHeightPx * s.resultRows.Length;
float btnTop = s.resultBoxSizePx.y - (s.resultButtonBottomPx + s.resultButtonSizePx.y);
N("배치 = 제목영역 " + top + "px + 행 " + s.resultRows.Length + "×" + s.resultRowHeightPx + "=" + rowsH +
"px → 본문 끝 " + (top + rowsH) + "px · 버튼 상단 " + btnTop + "px · 상자 " + s.resultBoxSizePx.y + "px");
Chk(top + rowsH <= btnTop, "행이 버튼을 안 침범(" + (top + rowsH) + " ≤ " + btnTop + ") = 잘림 0(산술)");
// ── ① 813w6 계약 확인 ────────────────────────────────────────────
H("① 813w6 페이로드 계약 — RunResult 에 보스 HP 3필드");
var probe = Fake(RunOutcome.Timeout, -1f, 0.2439f, 439d, 1800d);
N("합성 RunResult · bossHpRatio=" + probe.bossHpRatio.ToString("F4") + " bossHp=" + probe.bossHp + " bossHpMax=" + probe.bossHpMax);
Chk(Math.Abs(probe.bossHpRatio - 0.2439f) < 1e-5f, "RunResult.bossHpRatio 필드 존재·대입(컴파일이 곧 증거)");
Chk(probe.bossHp == 439d && probe.bossHpMax == 1800d, "RunResult.bossHp / bossHpMax 필드 존재·대입");
N("구독자 수 RunEnded=" + RunEvents.RunEnded.Count + " RunStarted=" + RunEvents.RunStarted.Count);
// ── ② 가짜 RunEnded(0.2439) → 「HP 남음 24 %」 ────────────────────
H("② 가짜 RunEnded(bossHpRatio 0.2439) → 「미처치 (HP 남음 24 %)」");
if (!RunResultUI.Subscribed) RunResultUI.Subscribe(true);
RunResultUI.ResetState();
BossHpBar.RunBossHpRatio = -1f; // 바는 아무것도 안 물었다 = 폴백이 못 쓰는 상태
RunResultUI.CopyResult(in probe); // = OnRunEnded 가 즉시 하는 그 복사
N("BuildBody → " + RunResultUI.BuildBody(s));
string v2 = BodyRow(s.rowBossClearLabel);
N("보스 처치 행 = \"" + v2 + "\" · 원천 = " + RunResultUI.LastBossHpSource +
" · 스냅샷 = " + (RunResultUI.BossHpRatioAtEnd * 100f).ToString("F2") + "% (" +
RunResultUI.BossHpAtEnd + "/" + RunResultUI.BossHpMaxAtEnd + ")");
Chk(v2 == "미처치 (HP 남음 24 %)", "문구 = \"미처치 (HP 남음 24 %)\" (0.2439 → 24)");
Chk(RunResultUI.LastBossHpSource == "페이로드", "원천 = 페이로드(BossHpBar 를 안 읽었다)");
Chk(Math.Abs(RunResultUI.BossHpRatioAtEnd - 0.2439f) < 1e-5f, "BossHpRatioAtEnd = 0.2439(구조체째 복사)");
Chk(RunResultUI.BossHpAtEnd == 439d && RunResultUI.BossHpMaxAtEnd == 1800d, "BossHp/BossHpMax 스냅샷도 함께 복사");
// ── ③ 🔴 부활 미룸 = 늦게 그려도 스냅샷 유지 ───────────────────────
H("③ 🔴 부활 미룸(늦은 그리기) — Park 로 만피가 되어도 값 불변");
BossArena.SetBossHpForProbe(1f, 1800d, 1800d); // Park → On_Regen 만피
BossHpBar.RunBossHpRatio = 1f; // 813q2 가 읽던 UI 미러도 만피
int deferBefore = BossArena.DeferredParkCount;
BossArena.PumpDeferredPark(); // 813w6 이 발행 뒤로 미뤄 둔 고정을 실제로 실행
N("Park 실행 DeferredParkCount " + deferBefore + " → " + BossArena.DeferredParkCount +
" · BossHpBar.RunBossHpRatio = " + BossHpBar.RunBossHpRatio.ToString("F2") +
" · BossArena.LastBossHpRatio = " + BossArena.LastBossHpRatio.ToString("F2"));
string show3 = RunResultUI.Show(); // ← 부활 팝업이 닫힌 뒤의 「늦은 그리기」
N("늦은 Show() → " + show3);
string v3 = BodyRow(s.rowBossClearLabel);
N("보스 처치 행(늦게) = \"" + v3 + "\" · 원천 = " + RunResultUI.LastBossHpSource);
Chk(v3 == "미처치 (HP 남음 24 %)", "🔴 늦게 그려도 24 % 그대로(813w6 §6-1 잔여 해소)");
Chk(Math.Abs(RunResultUI.BossHpRatioAtEnd - 0.2439f) < 1e-5f, "스냅샷 버퍼가 만피에 안 덮인다");
H("③-B C8 — resultBossHpFromPayload = 0 이면 같은 자리에서 100 %(D-44 재현)");
s.resultBossHpFromPayload = false;
RunResultUI.BuildBody(s);
string v3b = BodyRow(s.rowBossClearLabel);
N("행 = \"" + v3b + "\" · 원천 = " + RunResultUI.LastBossHpSource);
Chk(v3b == "미처치 (HP 남음 100 %)", "스위치 off → 813q2 동작(만피 100 %) = D-44 재현");
Chk(RunResultUI.LastBossHpSource == "UI바", "off 일 때 원천 = UI바(BossHpBar)");
s.resultBossHpFromPayload = true;
RunResultUI.BuildBody(s);
Chk(BodyRow(s.rowBossClearLabel) == "미처치 (HP 남음 24 %)", "스위치 복구 → 다시 24 %");
// ── ④ 진짜 RunDirector.EndRun 경로 ─────────────────────────────────
H("④ 진짜 발행 경로 — RunDirector.StartRun → EndRun(Timeout)");
BossArena.SetBossHpForProbe(0.2439f, 439d, 1800d);
BossHpBar.RunBossHpRatio = -1f;
RunResultUI.ResetState();
RunDirector.StartRun("WL813q3 프로브");
int endedBefore = RunResultUI.EndedSeen; // StartRun 이 앞 런을 Abandon 으로 닫을 수 있다
N("StartRun 후 phase=" + RunDirector.Phase + " running=" + RunDirector.IsRunning +
" (EndedSeen=" + endedBefore + ")");
if (RunDirector.IsRunning)
{
RunDirector.EndRun(RunOutcome.Timeout);
N("EndRun 후 EndedSeen " + endedBefore + " → " + RunResultUI.EndedSeen +
" · LastResult.bossHpRatio=" + RunDirector.LastResult.bossHpRatio.ToString("F4") +
" · 결과창 스냅샷=" + RunResultUI.BossHpRatioAtEnd.ToString("F4"));
string v4 = BodyRow(s.rowBossClearLabel);
N("보스 처치 행(진짜 발행) = \"" + v4 + "\" · 원천 = " + RunResultUI.LastBossHpSource);
Chk(RunResultUI.EndedSeen == endedBefore + 1, "결과창이 RunEnded 를 실제로 받았다");
Chk(Math.Abs(RunResultUI.BossHpRatioAtEnd - RunDirector.LastResult.bossHpRatio) < 1e-6f,
"결과창 스냅샷 = RunDirector.LastResult.bossHpRatio (같은 값)");
if (RunDirector.LastResult.bossHpRatio >= 0f)
{
Chk(v4 == "미처치 (HP 남음 24 %)", "진짜 발행에서도 24 %");
// 발행 뒤 Park → 다시 그려도 그대로
BossArena.SetBossHpForProbe(1f, 1800d, 1800d);
BossHpBar.RunBossHpRatio = 1f;
BossArena.PumpDeferredPark();
RunResultUI.BuildBody(s);
Chk(BodyRow(s.rowBossClearLabel) == "미처치 (HP 남음 24 %)", "진짜 발행 + Park 뒤 늦은 그리기도 24 %");
}
else
{
Note("BossArena 가 에디트 모드에서 비활성(snapshotBossHpOnRunEnd/Active) — 페이로드 1 · " +
"값 경로는 ②③ 합성으로만 실측(진짜 발행의 값은 「미확인」)");
}
}
else
{
Note("RunDirector 가 에디트 모드에서 안 돈다(WLRunSettings/Cfg) — 진짜 발행 경로는 「미확인」");
}
// ── ⑤ 처치 케이스 · 값 없음 폴백 ───────────────────────────────────
H("⑤ 처치 케이스(MM:SS) · 페이로드 1 폴백 · 둘 다 없음");
RunResultUI.CopyResult(Fake(RunOutcome.Win, 154f, 0f, 0d, 1800d));
RunResultUI.BuildBody(s);
string v5a = BodyRow(s.rowBossClearLabel);
N("처치 런(bossClearSec 154) = \"" + v5a + "\"");
Chk(v5a == "02:34", "처치면 「02:34」(HP 는 안 본다)");
BossHpBar.RunBossHpRatio = 0.436f; // 813q2 가 읽던 값이 살아 있는 상태
RunResultUI.CopyResult(Fake(RunOutcome.Timeout, -1f, -1f, 0d, 0d));
RunResultUI.BuildBody(s);
string v5b = BodyRow(s.rowBossClearLabel);
N("페이로드 1 + 바 43.6 % = \"" + v5b + "\" · 원천 = " + RunResultUI.LastBossHpSource);
Chk(v5b == "미처치 (HP 남음 44 %)", "페이로드가 1 이면 813q2 폴백(44 %)");
Chk(RunResultUI.LastBossHpSource == "UI바", "폴백 원천 표시 = UI바");
BossHpBar.RunBossHpRatio = -1f;
RunResultUI.BuildBody(s);
string v5c = BodyRow(s.rowBossClearLabel);
N("둘 다 없음 = \"" + v5c + "\" · 원천 = " + RunResultUI.LastBossHpSource);
Chk(v5c == s.resultBossNotClearedText, "둘 다 없으면 퍼센트 없이 「미처치」");
Chk(RunResultUI.LastBossHpSource == "없음", "원천 = 없음");
// ── ⑥ 결과창 행 실측 ───────────────────────────────────────────────
H("⑥ 결과창 행 실측 — 엘리트 · 카드 선택 · 사망/물약 · 본문 전체");
N("813o ZonePickup.PicksThisRun(현재) = " + ZonePickup.PicksThisRun + " · PickCount=" + ZonePickup.PickCount);
RunResultUI.CopyResult(Fake(RunOutcome.Timeout, -1f, 0.2439f, 439d, 1800d));
RunResultUI.BuildBody(s);
N("카드 선택 종료스냅 = " + RunResultUI.CardPicksAtEnd + " · 엘리트 종료스냅 = " + RunResultUI.EliteKillsAtEnd);
N("카드 선택 행 = \"" + BodyRow(s.rowCardPicksLabel) + "\" · 엘리트 행 = \"" + BodyRow(s.rowEliteKillsLabel) +
"\" · 사망/물약 행 = \"" + BodyRow(s.rowDeathsLabel) + "\"");
Chk(BodyRow(s.rowCardPicksLabel) == RunResultUI.CardPicksAtEnd.ToString(), "카드 선택 행 = 종료 스냅샷 값");
Chk(BodyRow(s.rowEliteKillsLabel) == RunResultUI.EliteKillsAtEnd.ToString(), "엘리트 행 = 종료 스냅샷 값(813q2 그대로)");
Chk(BodyRow(s.rowDeathsLabel) == "1 / 2", "사망/물약 행 = 「1 / 2」(RunResult 값 그대로)");
Chk(RunResultUI.VisibleRows == s.resultRows.Length, "본문 행 수 = resultRows 길이 (" + RunResultUI.VisibleRows + ")");
N("본문 = " + RunResultUI.BodyText.Replace("\n", " | "));
// 카드 선택 스냅샷이 「런 종료 순간」인지 — 뒤에 카운터가 늘어도 화면은 안 바뀐다
N("(카드 카운터는 813o 소유 · 프로브는 읽기만 — 스냅샷 시점만 확인한다)");
// ── ⑦ GC ───────────────────────────────────────────────────────────
H("⑦ GC — 매 프레임 경로 0 B · 이번에 늘어난 줄 0 B");
RunResultUI.Tick(0f); RunResultUI.BuildBody(s); // 워밍업(첫 회 버퍼)
long g0 = GC.GetTotalMemory(false);
for (int i = 0; i < 100; i++) RunResultUI.Tick(i);
long g1 = GC.GetTotalMemory(false);
Chk(g1 - g0 == 0, "Tick ×100(매 프레임 경로) Δ = " + (g1 - g0) + " B");
int sink = 0; float fsink = 0f;
g0 = GC.GetTotalMemory(false);
for (int i = 0; i < 100; i++) { sink += ZonePickup.PicksThisRun; fsink += RunResultUI.BossHpRatioAtEnd; }
g1 = GC.GetTotalMemory(false);
Chk(g1 - g0 == 0, "이번에 늘어난 읽기(PicksThisRun + BossHpRatioAtEnd) ×100 Δ = " + (g1 - g0) + " B (sink " + sink + "/" + fsink.ToString("F1") + ")");
g0 = GC.GetTotalMemory(false);
for (int i = 0; i < 100; i++) RunResultUI.LastBossHpSource = "페이로드";
g1 = GC.GetTotalMemory(false);
Chk(g1 - g0 == 0, "LastBossHpSource 대입(리터럴) ×100 Δ = " + (g1 - g0) + " B");
g0 = GC.GetTotalMemory(false);
for (int i = 0; i < 100; i++) RunResultUI.BuildBody(s);
g1 = GC.GetTotalMemory(false);
N("참고 — BuildBody ×100 Δ = " + (g1 - g0) + " B (런 종료 1회 경로 · 813q 부터 본문 문자열을 만든다 · 매 프레임 아님)");
// ── ⑧ C8 롤백 ──────────────────────────────────────────────────────
H("⑧ C8 롤백 — 값 하나씩");
s.resultCountCardPicksInUi = false;
RunResultUI.BuildBody(s);
Chk(BodyRow(s.rowCardPicksLabel) == "0", "resultCountCardPicksInUi=0 → 카드 선택 행 「0」");
s.resultCountCardPicksInUi = true;
s.resultRows = new WLRunResultRow[] { WLRunResultRow.TotalTime, WLRunResultRow.BossClear };
RunResultUI.BuildBody(s);
Chk(RunResultUI.VisibleRows == 2 && BodyRow(s.rowCardPicksLabel) == "", "resultRows 에서 CardPicks 를 빼면 행이 사라진다");
s.resultRows = o_rows;
RunResultUI.BuildBody(s);
Chk(RunResultUI.VisibleRows == o_rows.Length, "행 배열 복구 = " + RunResultUI.VisibleRows + "행");
s.resultBossHpFromPayload = false; s.resultBossHpFromUi = false;
RunResultUI.BuildBody(s);
Chk(BodyRow(s.rowBossClearLabel) == s.resultBossNotClearedText, "둘 다 off → 퍼센트 없이 「미처치」");
s.resultBossHpFromPayload = o_payload; s.resultBossHpFromUi = o_fromUi;
WLRunUiSettings.RuntimeDisabled = true;
string tickOff = RunResultUI.Tick(0f);
N("RuntimeDisabled Tick → " + tickOff);
Chk(tickOff.StartsWith("off"), "RuntimeDisabled=1 → Tick 무동작");
WLRunUiSettings.RuntimeDisabled = false;
Chk(RunResultUI.Tick(0f) == "tick ok", "복구 → tick ok");
s.resultEnabled = false;
Chk(RunResultUI.Tick(0f).StartsWith("off"), "resultEnabled=0 → 무동작");
s.resultEnabled = o_result;
// ── ⑨ 뒷정리 확인 ──────────────────────────────────────────────────
H("⑨ 뒷정리 — 에셋 저장 0 · 구독·주입 원복");
BossArena.SetBossHpForProbe(-2f, 0d, 0d); // 주입 해제
BossHpBar.RunBossHpRatio = o_barRatio;
RunResultUI.ResetState();
if (!o_subscribed) RunResultUI.Subscribe(false);
N("BossArena 주입 해제 · BossHpBar.RunBossHpRatio=" + BossHpBar.RunBossHpRatio +
" · 결과창 구독=" + RunResultUI.Subscribed + "(시작 시 " + o_subscribed + ")");
N("BossArena.BossHpSnapshots=" + BossArena.BossHpSnapshots + " DeferredParkCount=" + BossArena.DeferredParkCount);
}
catch (Exception ex)
{
_fail++;
_o.AppendLine(" [FAIL] 예외 — " + ex.GetType().Name + " : " + ex.Message);
_o.AppendLine(ex.StackTrace);
}
finally
{
// 🔴 SO 원복(메모리만 · 저장 0)
s.resultBossHpFromPayload = o_payload; s.resultBossHpFromUi = o_fromUi;
s.resultCountCardPicksInUi = o_cards; s.resultCountElitesInUi = o_elites;
s.enabled_ = o_enabled; s.resultEnabled = o_result; s.resultRows = o_rows;
WLRunUiSettings.RuntimeDisabled = false;
try { BossArena.SetBossHpForProbe(-2f, 0d, 0d); } catch { }
BossHpBar.RunBossHpRatio = o_barRatio;
}
var fi1 = new FileInfo(abs); fi1.Refresh();
N("에셋 후 = " + fi1.Length + " B · " + fi1.LastWriteTimeUtc.ToString("yyyy-MM-dd HH:mm:ss") + " UTC");
Chk(fi1.Length == len0 && fi1.LastWriteTimeUtc == mt0, "에셋 파일 무변동 = 저장 0(SaveAssets/SetDirty 호출 0)");
N("SO 값 원복 = payload:" + s.resultBossHpFromPayload + " fromUi:" + s.resultBossHpFromUi +
" cards:" + s.resultCountCardPicksInUi + " elites:" + s.resultCountElitesInUi +
" enabled_:" + s.enabled_ + " result:" + s.resultEnabled + " 행수:" + s.resultRows.Length);
return Flush();
}
static string Flush()
{
_o.AppendLine();
_o.AppendLine("== 합계 PASS " + _pass + " / FAIL " + _fail + " / NOTE " + _note + " ==");
string p = OutPath("WL813q3_PROBE.txt");
try { File.WriteAllText(p, _o.ToString(), new UTF8Encoding(true)); } catch { }
return _o.ToString() + "\n(파일: " + p + ")";
}
}