// ─────────────────────────────────────────────────────────────────────────────
// ZonePickup.cs — 존 클리어 픽업 카드(로그라이크 3장 중 1장) (WL-813o · #813)
//
// 기준서 §D-2 813o 행 + 기획안 F-3 + 발주서 WL-813o §1(Lead 설계 결정).
// 트리거 계약 = 813p 완료보고 §4 `RunEvents.ZoneCleared`(`Assets/WL/Combat/Run/RunEvents.cs` · 읽기만)
// → 원본 `MobControlMgr` 전멸 훅 **0**.
//
// ■ 🔴 실측(2026-09-09 · 발주서 §1-1 과 다른 곳) — **원본 `SelectSkillUI` 노드는 존재하지 않는다.**
// · `SelectSkillUI.cs` 스크립트 GUID `92b16098987b86b4d850255ef6a56f64` 는 `Assets` 전수 검색에서
// **자기 `.meta` 한 곳에서만** 나온다(어떤 `.prefab`/`.unity` 에도 붙어 있지 않다).
// · 상위 `GameUI`(GUID `abec0a6a…`)도 씬 27개·`Res_Addr`·`ResWork`·`Resources` 어디에도 없다
// → `GameUI.Ins` 는 항상 null → `MyActor.cs:162` 의 자동전투 정지 배선은 **작동한 적이 없다**.
// · 원본 카드 UI 는 **NGUI**(`UILabel`/`UISprite`)인데 실제 인게임 UI(`Res_Addr/MainUI/NewGameUI.prefab`)에는
// NGUI 컴포넌트가 **0개**다(UIPanel/UIRoot/UICamera/UILabel/UISprite 전부 0) → 붙여도 렌더링되지 않는다.
// 그래서 **가장 가까운 방식**으로 간다 —
// ① 원본 노드가 있으면 그대로 연다(`SelectSkillUI.Set` / `SelectSkillCard.OnClick_SelectSkill`
// 전부 이미 public · `Assets/Script/**` 수정 **0줄**). 없으면 ②.
// ② 카드 데이터·효과·자동전투 정지를 **전부 원본 경로 그대로** 쓰고 표시만 런타임 uGUI 로 만든다:
// 뽑기 = `table_selectskill.Ins.Get_DataList(n)`(원본 셔플·중복 제외·PreSkill 규칙 그대로)
// 효과 = `InGameInfo.Ins.Add_SelectSkill(id)` (원본 `SelectSkillUI.SkillSelect` 59행과 같은 줄)
// 정지 = `ActorInfo.Ins.All_Stop(true/false)` (원본 `SelectSkillUI` 29·61행과 같은 줄)
//
// ■ 순서 — ① 813w `RunGuide` 가 다음 존으로 이동 중(Travel)이면 **도착 뒤에** 연다(최대 `maxGuideDelaySeconds`).
// ② 813i/813z 부활 팝업이 떠 있으면 **부활이 우선**이고 카드는 미룬다(`deferWhileRevive`).
//
// ■ 런 타이머 — 카드가 열린 동안 813p `RunDirector.PauseTimer(true)`(세계 정지 없음 · 전투는 계속).
// `holdWorld` 를 켜면 813z `DeathFlow.HoldWorld` 와 같은 `timeScale=0` 방식으로도 갈 수 있다(기본 off).
//
// ■ GC 0 — 남은 초는 **표시값이 바뀐 초에만** `SetText(format, float)`(TMP 무할당 오버로드)로 쓴다.
// 카드 문자열은 열 때 1회만 쓴다. 로그 문자열은 **만들기 전에** `verboseLog` 로 막는다(811b FIX-3 규약).
//
// ■ 프리팹 diff 0 — 노드는 전부 런타임 생성(`HideFlags.DontSave`) · 1회 만들고 재사용.
// ■ C8 롤백 — `WLZonePickupSettings.enabled_ = 0` → 구독 0 · 노드 0 · 표시 0 · 타이머 정지 0.
// 🔴 어셈블리 주의: Assets/WL/UI/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
// ─────────────────────────────────────────────────────────────────────────────
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using WL.Combat.Run;
using WL.Combat.Survival;
namespace WL.UI
{
/// 픽업 카드의 진행 단계.
public enum ZonePickupPhase
{
/// 대기 — 예약 없음.
Idle = 0,
/// 존 클리어를 받아 열기를 예약했다(지연·가이드·부활 대기).
Pending = 1,
/// 카드가 열려 있다.
Open = 2,
}
/// 존 클리어 픽업 카드(정적 · 노드는 런타임 생성 · 프리팹 diff 0).
public static class ZonePickup
{
// ── 노드 ──────────────────────────────────────────────────────────────
private static Transform s_uiRoot;
private static RectTransform s_root;
private static CanvasGroup s_group;
private static Canvas s_canvas; // WL-813c2(D-29) — 그리는 순서를 값으로 고정
private static GraphicRaycaster s_raycaster; // overrideSorting 캔버스는 자기 레이캐스터가 있어야 눌린다
private static Image s_dim;
private static RectTransform s_box;
private static Image s_boxBg;
private static TextMeshProUGUI s_title;
private static TextMeshProUGUI s_timer;
private static RectTransform s_cardRoot;
private static RectTransform[] s_card = new RectTransform[0];
private static Image[] s_cardBg = new Image[0];
private static TextMeshProUGUI[] s_cardName = new TextMeshProUGUI[0];
private static TextMeshProUGUI[] s_cardDesc = new TextMeshProUGUI[0];
private static Button[] s_cardButton = new Button[0];
private static TMP_FontAsset s_font;
// ── 상태 ──────────────────────────────────────────────────────────────
private static bool s_subscribed;
private static ZonePickupPhase s_phase;
private static float s_pendingSince = -1f; // 예약 시각(unscaled)
private static float s_openAt = -1f; // 열린 시각
private static int s_pendingZone = -1;
private static int s_picksThisRun;
private static bool s_timerPaused, s_worldHeld, s_actorsStopped;
private static float s_prevTimeScale = 1f;
private static int s_shownSec = int.MinValue;
private static bool s_shownWarn;
private static bool s_usedOriginalUi;
// ── WL-813c2 (Q6 D-29) — 부활 팝업이 카드를 덮는 동안 타임아웃 정지 ────
private static float s_pausedFor; // 부활 때문에 멈춰 있던 누적 시간(초)
private static float s_lastTickNow = -1f;// 직전 틱 시각(정지 구간을 dt 로 적분한다)
private static bool s_reviveHeld; // 지금 부활 때문에 멈춰 있는가
/// 이번에 뽑힌 카드의 스킬 ID(0 = 빈 칸). 폴백 패널 경로에서만 채운다.
private static readonly List s_ids = new List(8);
private static readonly List