2026-09-08 17:13:35 +00:00
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
// ReviveDialog.cs — 사망 시 부활 팝업 (WL-813tj · 813j · #813)
|
|
|
|
|
//
|
|
|
|
|
// 기준서 v1 §D-1 813j 행: "사망 시 부활 팝업(공용 팝업 SortOrder_5 재사용 ·
|
|
|
|
|
// '존 시작점에서 부활' · 비용 항목은 PD BM 전까지 무료)"
|
|
|
|
|
// §B 요소 8: 사망 연출 2 s → 부활(존 시작점 · 비용 = PD BM).
|
|
|
|
|
//
|
|
|
|
|
// ■ 🔴 사망 통지는 813i(Gameplay) 가 준다 — 실측 근거
|
|
|
|
|
// `CombatEvents.RaiseKilled` 의 호출처는 `Assets/Script/Character/Mob/MobActor.cs:435` **하나뿐**이라
|
|
|
|
|
// PC 사망은 어떤 811 이벤트로도 오지 않는다(2026-09-09 실측 · grep 전수).
|
|
|
|
|
// `wl/gameplay/WL-813i-survival` 브랜치는 main 대비 커밋 0 = `WL.Combat.Survival.DeathFlow` 도 없다.
|
|
|
|
|
// 그래서 존재하지 않는 타입을 참조하지 않고 **정적 진입점**만 열어 둔다(「후속」):
|
|
|
|
|
// WL.UI.ReviveDialog.NotifyDeath(); // 813i DeathFlow 가 사망 연출 뒤 1줄
|
|
|
|
|
// WL.UI.ReviveDialog.ReviveRequested = () => ...; // 813i 가 존 시작점 On_Regen 을 붙인다
|
|
|
|
|
// 813i 없이도 팝업은 "표시 + 확인" 까지 완전히 동작한다(부활 동작만 비어 있다).
|
|
|
|
|
//
|
|
|
|
|
// ■ 공용 팝업 재사용
|
|
|
|
|
// `Assets/Script/Info/Popup.cs`(싱글턴 `Popup.Ins` · Addressables 프리팹 `SortOrder_5` 안 · 802b 가
|
|
|
|
|
// 같은 프리팹에 SafeAreaFitter 를 붙였다)를 그대로 쓴다. 문구는 localtext 키가 없으므로
|
|
|
|
|
// `Set()` 직후 `label_msg.text` 를 설정 문자열로 덮는다(원본 Popup.cs **무수정**).
|
|
|
|
|
// 공용 팝업이 없거나(씬 미로드) 표가 없으면 자체 폴백 노드로 떨어진다.
|
|
|
|
|
//
|
|
|
|
|
// ■ 값 = WLSurvivalUiSettings.asset (C45) · 시간 = Time.unscaledTime
|
|
|
|
|
// 🔴 어셈블리 주의: Assets/WL/UI/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
2026-09-09 00:23:06 +00:00
|
|
|
// ■ WL-813z (Q4-a D-3 · D-9) — 2026-09-09
|
|
|
|
|
// D-3 실측: 공용 `Popup` 은 **노드 1개짜리 싱글턴**(`Assets/Script/Info/Popup.cs` 전문 54줄)이고
|
|
|
|
|
// `Set()` 이 `m_ok` 를 매번 덮어쓴다. `Util/ErrorLogHookManager.cs:33` 이 Error/Exception 마다
|
|
|
|
|
// `Popup.Ins.Set(One, 174, null, null, log)` 을 부르므로 **부활 팝업이 넘긴 확인 콜백이 null 로 지워진다**
|
|
|
|
|
// → 확인을 눌러도 부활이 안 되고 `timeScale=0` 이 영영 안 풀린다(Q4-a 슬롯① 2분 30초).
|
|
|
|
|
// 대응 3층: ① 813i DeathFlow.reviveWatchdogSeconds(자동 부활 · 마지막 방어선)
|
|
|
|
|
// ② 전용 노드를 최상단 캔버스(sortingOrder > SortOrder_5 의 5)로 올려 확인 버튼을 항상 노출
|
|
|
|
|
// ③ 공용 Popup 이 남의 내용으로 바뀌면 감지해 전용 팝업으로 **승격**
|
|
|
|
|
// 부수 수정: `HideNow()` 가 **우리 것일 때만** 공용 Popup 도 닫는다(Q4-a QA-4 「팝업이 안 닫힌다」).
|
|
|
|
|
// D-9: 런타임 생성 확인 버튼 이름 `btn_ok` → `WL_btn_ok`(원본 `NightmareResult/btn_ok` 와 이름 충돌 제거).
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
using System.Text;
|
|
|
|
|
using TMPro;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UI;
|
2026-09-09 00:23:06 +00:00
|
|
|
using WL.Combat.Survival;
|
2026-09-08 17:13:35 +00:00
|
|
|
|
|
|
|
|
namespace WL.UI
|
|
|
|
|
{
|
|
|
|
|
[DisallowMultipleComponent]
|
|
|
|
|
[RequireComponent(typeof(RectTransform))]
|
|
|
|
|
public class ReviveDialog : MonoBehaviour
|
|
|
|
|
{
|
|
|
|
|
// ── 813i 연결점 (「후속」) ────────────────────────────────────────────
|
|
|
|
|
/// <summary>확인을 누르면 불린다. 813i 가 존 시작점 부활을 붙인다.</summary>
|
|
|
|
|
public static System.Action ReviveRequested;
|
|
|
|
|
/// <summary>팝업이 뜰 때 불린다(진단·튜토리얼용 · 선택).</summary>
|
|
|
|
|
public static System.Action ReviveShown;
|
|
|
|
|
|
2026-09-10 16:36:55 +00:00
|
|
|
// ── WL-815d 스테이지 실패 팝업 (기준서 §E) ───────────────────────────
|
|
|
|
|
// 스테이지 중 실패(사망 · 60 s 초과)는 「부활」 1버튼이 아니라 **「다시 도전 / 로비로」 2버튼**이다.
|
|
|
|
|
// · 815b `WLStageSettings.bindReviveOkToRetry` 는 **0** 으로 내려간다(임시 배선 제거).
|
|
|
|
|
// · 자동 부활 0(`WLSurvivalSettings.reviveWatchdogSeconds = 0`) 은 815b 가 이미 내려 두었다.
|
|
|
|
|
// · 813z 3층 방어(전용 Canvas sortingOrder 60 · 재보장 · 탈취 승격)는 **그대로 살아 있다**.
|
|
|
|
|
// · 공용 Popup 경로는 쓰지 않는다 — 버튼 문구 2개를 프리팹 수정 없이 넣을 수 없기 때문(813q 실측과 같은 이유).
|
|
|
|
|
/// <summary>[다시 도전] — 대입되어 있으면 [확인] 대신 이것이 불린다(815d `StageUiBridge`).</summary>
|
|
|
|
|
public static System.Action PrimaryOverride;
|
|
|
|
|
/// <summary>[로비로] — 대입되어 있으면 두 번째 버튼이 생긴다.</summary>
|
|
|
|
|
public static System.Action SecondaryAction;
|
|
|
|
|
/// <summary>지금 스테이지 실패 팝업 모드인가(문구·버튼 2개).</summary>
|
|
|
|
|
public static bool StageMode { get; private set; }
|
|
|
|
|
public static int StagePrimaryCount, StageSecondaryCount, StageShownCount;
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
/// <summary>현재 씬에 살아 있는 인스턴스(정적 진입점이 찾는다).</summary>
|
|
|
|
|
public static ReviveDialog Active { get; private set; }
|
|
|
|
|
|
2026-09-09 00:15:05 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// 부활 팝업이 지금 떠 있거나 표시 예약 중인가(공용 Popup 경로 포함).
|
|
|
|
|
/// WL-813q 결과 화면이 「부활 팝업 우선」 순서를 지킬 때 본다(읽기 전용 · 동작 변경 0).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool Busy { get { return Active != null && (Active._open || Active._showAt >= 0f); } }
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
/// <summary>813i DeathFlow 가 사망 연출 뒤 부르는 진입점. 인스턴스가 없으면 무동작.</summary>
|
|
|
|
|
public static string NotifyDeath()
|
|
|
|
|
{
|
|
|
|
|
if (Active == null) return "ReviveDialog 인스턴스 없음 — 무동작";
|
|
|
|
|
return Active.QueueShow(Time.unscaledTime);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 22:58:46 +00:00
|
|
|
// ── WL-813y — 813i DeathFlow 와 이어 붙인 진입점 2개 ──────────────────
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// `DeathFlow.ReviveRequested` 진입점 — 813i 가 **사망 연출을 이미 기다렸으므로** 지연 없이 연다.
|
|
|
|
|
/// (`NotifyDeath()` 의 UI 지연과 겹쳐 두 번 뜨지 않도록 예약을 지우고 1회만 연다.)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string NotifyReviveRequested()
|
|
|
|
|
{
|
|
|
|
|
if (Active == null) return "ReviveDialog 인스턴스 없음 — 무동작";
|
|
|
|
|
return Active.ShowNowOnce();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>`DeathFlow.Revived` 진입점 — 부활이 끝났으니 팝업을 닫는다(자동/외부 부활 포함).</summary>
|
|
|
|
|
public static string NotifyRevived()
|
|
|
|
|
{
|
|
|
|
|
if (Active == null) return "ReviveDialog 인스턴스 없음 — 무동작";
|
|
|
|
|
Active.HideNow();
|
|
|
|
|
return "부활 완료 — 팝업 닫음";
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
[Header("폴백 팝업 구성 요소 (공용 Popup 이 없을 때만 쓴다)")]
|
|
|
|
|
[SerializeField] private CanvasGroup group;
|
|
|
|
|
[SerializeField] private RectTransform box;
|
|
|
|
|
[SerializeField] private Image dim;
|
|
|
|
|
[SerializeField] private Image boxBg;
|
|
|
|
|
[SerializeField] private TextMeshProUGUI messageLabel;
|
|
|
|
|
[SerializeField] private Button okButton;
|
|
|
|
|
[SerializeField] private TextMeshProUGUI okLabel;
|
2026-09-10 16:36:55 +00:00
|
|
|
// WL-815d — 스테이지 실패 팝업의 두 번째 버튼(기준서 §E 「다시 도전 / 로비로」)
|
|
|
|
|
[SerializeField] private Button secondaryButton;
|
|
|
|
|
[SerializeField] private TextMeshProUGUI secondaryLabel;
|
2026-09-08 17:13:35 +00:00
|
|
|
|
|
|
|
|
[Header("폰트 (에디터가 주입)")]
|
|
|
|
|
[SerializeField] private TMP_FontAsset font;
|
|
|
|
|
|
|
|
|
|
private RectTransform _rt;
|
|
|
|
|
private Canvas _canvas;
|
|
|
|
|
private float _showAt = -1f;
|
2026-09-08 22:58:46 +00:00
|
|
|
private bool _open; // 팝업이 열려 있는가(공용 Popup 경로 포함 · WL-813y)
|
2026-09-08 17:13:35 +00:00
|
|
|
|
2026-09-09 00:23:06 +00:00
|
|
|
// ── WL-813z(D-3) 최상단 보장 ──────────────────────────────────────────
|
|
|
|
|
/// <summary>런타임 생성 확인 버튼 이름 — 원본 `NightmareResult/btn_ok` 와 겹치지 않게 접두어를 붙인다(D-9).</summary>
|
|
|
|
|
public const string OkButtonName = "WL_btn_ok";
|
2026-09-10 16:36:55 +00:00
|
|
|
/// <summary>WL-815d — 두 번째 버튼 이름(같은 D-9 규약으로 `WL_` 접두).</summary>
|
|
|
|
|
public const string SecondaryButtonName = "WL_btn_second";
|
2026-09-09 00:23:06 +00:00
|
|
|
private Canvas _ownCanvas;
|
|
|
|
|
private GraphicRaycaster _ownRaycaster;
|
|
|
|
|
private float _nextTopmostAt = -1f;
|
|
|
|
|
private bool _escalated; // 공용 Popup 이 덮여 전용 팝업으로 승격했다
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
// ── 진단 ──────────────────────────────────────────────────────────────
|
|
|
|
|
public int DeathSeen { get; private set; }
|
|
|
|
|
public int ShownCount { get; private set; }
|
|
|
|
|
public int ConfirmCount { get; private set; }
|
|
|
|
|
public bool UsedCommonPopup { get; private set; }
|
|
|
|
|
public string LastMessage { get; private set; } = "";
|
|
|
|
|
public bool FallbackVisible { get { return group != null && group.alpha > 0.001f; } }
|
|
|
|
|
public bool Pending { get { return _showAt >= 0f; } }
|
2026-09-09 00:23:06 +00:00
|
|
|
/// <summary>WL-813z — 최상단 재보장 횟수 · 공용 Popup 탈취 감지 횟수 · 우리가 닫은 공용 Popup 수.</summary>
|
|
|
|
|
public int TopmostCount { get; private set; }
|
|
|
|
|
public int HijackCount { get; private set; }
|
|
|
|
|
public int CommonPopupClosed { get; private set; }
|
|
|
|
|
public bool Escalated { get { return _escalated; } }
|
|
|
|
|
public bool IsOpen { get { return _open; } }
|
|
|
|
|
public int SortingOrder { get { return _ownCanvas != null && _ownCanvas.overrideSorting ? _ownCanvas.sortingOrder : -1; } }
|
|
|
|
|
public bool IsLastSibling { get { return _rt != null && _rt.parent != null && _rt.GetSiblingIndex() == _rt.parent.childCount - 1; } }
|
2026-09-08 17:13:35 +00:00
|
|
|
|
|
|
|
|
private void Awake() { _rt = GetComponent<RectTransform>(); Active = this; }
|
|
|
|
|
private void OnEnable() { Active = this; Initialize(); }
|
|
|
|
|
private void OnDisable() { if (Active == this) Active = null; }
|
|
|
|
|
|
|
|
|
|
public string Initialize()
|
|
|
|
|
{
|
|
|
|
|
if (_rt == null) _rt = GetComponent<RectTransform>();
|
|
|
|
|
Active = this;
|
|
|
|
|
BuildIfNeeded();
|
|
|
|
|
string layout = ApplyLayout();
|
|
|
|
|
HideNow();
|
|
|
|
|
return "initialized · " + layout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>사망 통지 → 설정된 지연 뒤에 팝업을 연다(사망 연출 시간은 813i 소유).</summary>
|
|
|
|
|
public string QueueShow(float now)
|
|
|
|
|
{
|
|
|
|
|
DeathSeen++;
|
|
|
|
|
var s = WLSurvivalUiSettings.Instance;
|
|
|
|
|
if (s == null) return "WLSurvivalUiSettings 에셋 없음 — 무동작";
|
|
|
|
|
if (!s.reviveEnabled) return "reviveEnabled=false — 무동작";
|
|
|
|
|
_showAt = now + Mathf.Max(0f, s.reviveDelaySeconds);
|
|
|
|
|
if (s.verboseLog) Debug.Log("[ReviveDialog] 사망 통지 — " + s.reviveDelaySeconds + "s 뒤 팝업");
|
|
|
|
|
return "예약 delay=" + s.reviveDelaySeconds.ToString("F2") + "s";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Update() { Tick(Time.unscaledTime); }
|
|
|
|
|
|
2026-09-09 00:23:06 +00:00
|
|
|
/// <summary>지연 만료 검사 + 표시 중 최상단 재보장. 프로브가 시간을 넣어 같은 경로로 검증한다.</summary>
|
2026-09-08 17:13:35 +00:00
|
|
|
public void Tick(float now)
|
|
|
|
|
{
|
2026-09-09 00:23:06 +00:00
|
|
|
// ── WL-813z(D-3) 표시 중 재보장(GC 0 — 문자열·할당 없음 · 바뀐 값만 쓴다)
|
|
|
|
|
if (_open)
|
|
|
|
|
{
|
|
|
|
|
var g = WLSurvivalSettings.Instance;
|
|
|
|
|
if (g != null && g.reviveTopmostEnabled && g.reviveTopmostIntervalSeconds > 0f && now >= _nextTopmostAt)
|
|
|
|
|
{
|
|
|
|
|
_nextTopmostAt = now + g.reviveTopmostIntervalSeconds;
|
|
|
|
|
EnsureTopmost(g);
|
|
|
|
|
CheckCommonPopupHijack(g);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
if (_showAt < 0f || now < _showAt) return;
|
|
|
|
|
_showAt = -1f;
|
|
|
|
|
ShowNow();
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 00:23:06 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// WL-813z(D-3) — 전용 노드를 형제 맨 뒤 + 전용 캔버스(sortingOrder = SO)로 올리고 레이캐스트를 살린다.
|
|
|
|
|
/// 공용 팝업 프리팹 `SortOrder_5` 의 Canvas.m_SortingOrder = 5(실측)보다 커야 확인 버튼이 안 가린다.
|
|
|
|
|
/// 값이 이미 맞으면 아무 것도 쓰지 않는다(GC 0 · SetDirty 0).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void EnsureTopmost(WLSurvivalSettings g)
|
|
|
|
|
{
|
|
|
|
|
if (g == null || !g.reviveTopmostEnabled) return;
|
|
|
|
|
if (_rt == null) _rt = GetComponent<RectTransform>();
|
|
|
|
|
if (_rt == null) return;
|
|
|
|
|
|
|
|
|
|
var parent = _rt.parent;
|
|
|
|
|
if (parent != null && _rt.GetSiblingIndex() != parent.childCount - 1) _rt.SetAsLastSibling();
|
|
|
|
|
|
|
|
|
|
if (_ownCanvas == null)
|
|
|
|
|
{
|
|
|
|
|
_ownCanvas = GetComponent<Canvas>();
|
|
|
|
|
if (_ownCanvas == null) _ownCanvas = gameObject.AddComponent<Canvas>();
|
|
|
|
|
}
|
|
|
|
|
if (!_ownCanvas.overrideSorting) _ownCanvas.overrideSorting = true;
|
|
|
|
|
if (_ownCanvas.sortingOrder != g.reviveSortingOrder) _ownCanvas.sortingOrder = g.reviveSortingOrder;
|
|
|
|
|
|
|
|
|
|
// 전용 캔버스를 얹으면 부모 GraphicRaycaster 의 레이캐스트가 여기서 끊긴다 — 자기 것을 붙인다.
|
|
|
|
|
if (_ownRaycaster == null)
|
|
|
|
|
{
|
|
|
|
|
_ownRaycaster = GetComponent<GraphicRaycaster>();
|
|
|
|
|
if (_ownRaycaster == null) _ownRaycaster = gameObject.AddComponent<GraphicRaycaster>();
|
|
|
|
|
}
|
|
|
|
|
if (!_ownRaycaster.enabled) _ownRaycaster.enabled = true;
|
|
|
|
|
|
|
|
|
|
TopmostCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WL-813z(D-3) — 공용 Popup 이 남의 내용으로 바뀌었는지(= 우리 확인 콜백 m_ok 가 지워졌는지) 본다.
|
|
|
|
|
/// m_ok 는 private 이라 직접 못 읽으므로 **본문 텍스트와 활성 상태**로 판정한다(문자열 비교 = 할당 0).
|
|
|
|
|
/// 탈취를 확인하면 전용 폴백 팝업으로 승격한다 — 그쪽은 최상단 캔버스라 확인 버튼에 반드시 닿는다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void CheckCommonPopupHijack(WLSurvivalSettings g)
|
|
|
|
|
{
|
|
|
|
|
if (!g.reviveEscalateOnHijack || !UsedCommonPopup || _escalated) return;
|
|
|
|
|
bool lost;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var p = Popup.Ins;
|
|
|
|
|
if (p == null || p.m_popup == null || p.label_msg == null) lost = true;
|
|
|
|
|
else lost = !p.m_popup.activeSelf || !string.Equals(p.label_msg.text, LastMessage);
|
|
|
|
|
}
|
|
|
|
|
catch { lost = true; }
|
|
|
|
|
if (!lost) return;
|
|
|
|
|
|
|
|
|
|
HijackCount++;
|
|
|
|
|
_escalated = true;
|
|
|
|
|
UsedCommonPopup = false;
|
|
|
|
|
var s = WLSurvivalUiSettings.Instance;
|
|
|
|
|
if (s != null) ShowFallback(s, LastMessage);
|
|
|
|
|
Debug.Log("[ReviveDialog] 공용 Popup 이 다른 팝업에 덮였다 — 전용 부활 팝업으로 승격(D-3)");
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 22:58:46 +00:00
|
|
|
/// <summary>예약을 지우고, 아직 안 떠 있을 때만 연다(WL-813y · 813i 이중 통지 방지).</summary>
|
|
|
|
|
public string ShowNowOnce()
|
|
|
|
|
{
|
|
|
|
|
_showAt = -1f;
|
|
|
|
|
if (_open) return "이미 표시 중 — 중복 무시(shows=" + ShownCount + ")";
|
|
|
|
|
return ShowNow();
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
/// <summary>지금 바로 팝업을 연다(공용 Popup 우선 · 실패하면 폴백 노드).</summary>
|
|
|
|
|
public string ShowNow()
|
|
|
|
|
{
|
|
|
|
|
var s = WLSurvivalUiSettings.Instance;
|
|
|
|
|
if (s == null) return "WLSurvivalUiSettings 에셋 없음";
|
2026-09-08 22:58:46 +00:00
|
|
|
_open = true;
|
2026-09-08 17:13:35 +00:00
|
|
|
|
|
|
|
|
string msg = BuildMessage(s);
|
|
|
|
|
LastMessage = msg;
|
|
|
|
|
ShownCount++;
|
|
|
|
|
UsedCommonPopup = false;
|
2026-09-09 00:23:06 +00:00
|
|
|
_escalated = false;
|
|
|
|
|
|
|
|
|
|
// WL-813z(D-3) — 표시 순간 최상단 1회 + 재보장 시각 예약(공용 팝업 경로에서도 승격 대비로 올려 둔다)
|
|
|
|
|
var g = WLSurvivalSettings.Instance;
|
|
|
|
|
EnsureTopmost(g);
|
|
|
|
|
_nextTopmostAt = Time.unscaledTime + (g != null ? Mathf.Max(0f, g.reviveTopmostIntervalSeconds) : 0f);
|
2026-09-08 17:13:35 +00:00
|
|
|
|
2026-09-10 16:36:55 +00:00
|
|
|
// 🔴 WL-815d — 스테이지 실패는 버튼이 2개라 공용 Popup(문구 1개 · 프리팹 고정 버튼)으로 못 만든다.
|
|
|
|
|
if (s.reviveUseCommonPopup && !StageMode)
|
2026-09-08 17:13:35 +00:00
|
|
|
{
|
|
|
|
|
string r = TryCommonPopup(s, msg);
|
|
|
|
|
if (UsedCommonPopup)
|
|
|
|
|
{
|
|
|
|
|
if (ReviveShown != null) { try { ReviveShown(); } catch { } }
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ShowFallback(s, msg);
|
|
|
|
|
if (ReviveShown != null) { try { ReviveShown(); } catch { } }
|
|
|
|
|
return "폴백 팝업 표시 msg=\"" + msg + "\"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>본문 = 설정 문자열(로컬라이즈되면 reviveMessageKey 우선) + 비용 줄.</summary>
|
|
|
|
|
public string BuildMessage(WLSurvivalUiSettings s)
|
|
|
|
|
{
|
2026-09-10 16:36:55 +00:00
|
|
|
// WL-815d — 스테이지 실패 문구(사망 / 시간 초과)가 있으면 그것이 이긴다(비용 줄 없음).
|
|
|
|
|
if (StageMode && !string.IsNullOrEmpty(_msgOverride)) return _msgOverride;
|
2026-09-08 17:13:35 +00:00
|
|
|
string msg = s.reviveMessage;
|
|
|
|
|
if (s.reviveMessageKey > 0)
|
|
|
|
|
{
|
|
|
|
|
try { if (table_localtext.Ins != null) msg = table_localtext.Ins.Get_Text(s.reviveMessageKey); }
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
if (s.reviveShowCostLine && !string.IsNullOrEmpty(s.reviveCostText))
|
|
|
|
|
msg += "\n" + s.reviveCostText;
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>공용 Popup 싱글턴 재사용. 실패(미로드·표 없음)하면 UsedCommonPopup=false 로 남긴다.</summary>
|
|
|
|
|
private string TryCommonPopup(WLSurvivalUiSettings s, string msg)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (Popup.Ins == null) return "공용 Popup 없음 — 폴백";
|
|
|
|
|
Popup.Ins.Set(ePopupType.One, s.reviveMessageKey, OnOkListener); // Action = void 시그니처
|
|
|
|
|
if (Popup.Ins.label_msg != null) Popup.Ins.label_msg.text = msg; // 문구는 SO 가 주인
|
|
|
|
|
UsedCommonPopup = true;
|
|
|
|
|
return "공용 Popup(SortOrder_5) 표시 msg=\"" + msg + "\"";
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
UsedCommonPopup = false;
|
|
|
|
|
return "공용 Popup 실패(" + e.GetType().Name + ") — 폴백";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ShowFallback(WLSurvivalUiSettings s, string msg)
|
|
|
|
|
{
|
|
|
|
|
BuildIfNeeded();
|
|
|
|
|
ApplyLayout();
|
|
|
|
|
if (messageLabel != null) messageLabel.text = msg;
|
|
|
|
|
if (group != null) { group.alpha = 1f; group.blocksRaycasts = true; group.interactable = true; }
|
2026-09-09 00:23:06 +00:00
|
|
|
EnsureTopmost(WLSurvivalSettings.Instance); // WL-813z(D-3) — 승격 직후에도 최상단·레이캐스트 보장
|
2026-09-08 17:13:35 +00:00
|
|
|
}
|
|
|
|
|
|
2026-09-10 16:36:55 +00:00
|
|
|
/// <summary>확인 = 부활 요청. 813i 가 ReviveRequested 를 채우면 실제 부활이 일어난다.
|
|
|
|
|
/// 🔴 WL-815d: 스테이지 실패 팝업이면 [다시 도전](`StageDirector.Retry()`)이 대신 불린다.</summary>
|
2026-09-08 17:13:35 +00:00
|
|
|
public string OnConfirm()
|
|
|
|
|
{
|
|
|
|
|
ConfirmCount++;
|
2026-09-10 16:36:55 +00:00
|
|
|
bool stage = StageMode && PrimaryOverride != null;
|
2026-09-08 17:13:35 +00:00
|
|
|
HideNow();
|
2026-09-10 16:36:55 +00:00
|
|
|
if (stage)
|
|
|
|
|
{
|
|
|
|
|
StagePrimaryCount++;
|
|
|
|
|
try { PrimaryOverride(); } catch { }
|
|
|
|
|
return "다시 도전 — StageDirector.Retry() 전달";
|
|
|
|
|
}
|
2026-09-08 17:13:35 +00:00
|
|
|
if (ReviveRequested == null) return "확인 — 813i(ReviveRequested) 미연결이라 부활 동작 없음(후속)";
|
|
|
|
|
try { ReviveRequested(); } catch { }
|
|
|
|
|
return "확인 — 부활 요청 전달";
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-10 16:36:55 +00:00
|
|
|
/// <summary>WL-815d — 두 번째 버튼([로비로]).</summary>
|
|
|
|
|
public string OnSecondary()
|
|
|
|
|
{
|
|
|
|
|
HideNow();
|
|
|
|
|
if (SecondaryAction == null) return "두 번째 버튼 — 핸들러 미연결(무동작)";
|
|
|
|
|
StageSecondaryCount++;
|
|
|
|
|
try { SecondaryAction(); } catch { }
|
|
|
|
|
return "로비로 — 전달";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── WL-815d 스테이지 실패 팝업 진입점 ────────────────────────────────
|
|
|
|
|
private string _msgOverride, _okOverride, _secondOverride;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 스테이지 실패 팝업을 **지금** 연다(2버튼 · 전용 폴백 패널 고정 · 공용 Popup 미사용).
|
|
|
|
|
/// 사망 경로(DeathFlow → NotifyReviveRequested)로 이미 떠 있으면 문구·버튼만 갈아 끼운다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string ShowStageFail(string message, string okText, string secondaryText)
|
|
|
|
|
{
|
|
|
|
|
var s = WLSurvivalUiSettings.Instance;
|
|
|
|
|
if (s == null) return "WLSurvivalUiSettings 에셋 없음";
|
|
|
|
|
StageMode = true;
|
|
|
|
|
_msgOverride = message; _okOverride = okText; _secondOverride = secondaryText;
|
|
|
|
|
_showAt = -1f;
|
|
|
|
|
_open = true;
|
|
|
|
|
StageShownCount++;
|
|
|
|
|
ShownCount++;
|
|
|
|
|
LastMessage = message ?? "";
|
|
|
|
|
UsedCommonPopup = false; // 🔴 2버튼은 공용 Popup 으로 못 만든다 → 전용 패널
|
|
|
|
|
_escalated = false;
|
|
|
|
|
|
|
|
|
|
var g = WLSurvivalSettings.Instance;
|
|
|
|
|
EnsureTopmost(g); // 813z 3층 방어 그대로
|
|
|
|
|
_nextTopmostAt = Time.unscaledTime + (g != null ? Mathf.Max(0f, g.reviveTopmostIntervalSeconds) : 0f);
|
|
|
|
|
|
|
|
|
|
ShowFallback(s, LastMessage);
|
|
|
|
|
if (ReviveShown != null) { try { ReviveShown(); } catch { } }
|
|
|
|
|
return "스테이지 실패 팝업 2버튼 표시 · msg=\"" + LastMessage.Replace("\n", " / ") +
|
|
|
|
|
"\" [" + okText + "] [" + secondaryText + "] · sortingOrder=" + SortingOrder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>스테이지 실패 팝업 모드를 끈다(813z 원래 1버튼 부활 팝업으로 복귀).</summary>
|
|
|
|
|
public string ClearStageMode()
|
|
|
|
|
{
|
|
|
|
|
StageMode = false;
|
|
|
|
|
_msgOverride = _okOverride = _secondOverride = null;
|
|
|
|
|
ApplyLayout();
|
|
|
|
|
return "스테이지 실패 팝업 모드 해제 — 813z 원래대로(버튼 1개)";
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
public void HideNow()
|
|
|
|
|
{
|
|
|
|
|
_showAt = -1f;
|
2026-09-09 00:23:06 +00:00
|
|
|
bool wasOpen = _open;
|
2026-09-08 22:58:46 +00:00
|
|
|
_open = false;
|
2026-09-09 00:23:06 +00:00
|
|
|
_nextTopmostAt = -1f;
|
|
|
|
|
_escalated = false;
|
|
|
|
|
if (wasOpen) CloseCommonPopupIfOurs(); // WL-813z — QA-4 「부활했는데 공용 팝업이 남는다」
|
2026-09-08 17:13:35 +00:00
|
|
|
if (group == null) return;
|
|
|
|
|
group.alpha = 0f; group.blocksRaycasts = false; group.interactable = false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 00:23:06 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// WL-813z — 공용 Popup 이 **아직 우리 본문을 들고 있을 때만** 닫는다(남의 팝업은 건드리지 않는다).
|
|
|
|
|
/// `OnClick_Back()` = 원본 `ClosePopup()`(SetActive(false) + BackKeyMgr.Del_Back) + `m_no?.Invoke()`.
|
|
|
|
|
/// 우리는 `_no` 를 넘긴 적이 없고 본문이 우리 것이면 `Set()` 이 다시 안 불린 것이라 `m_no` 는 null 이다(Popup.cs:14 실측).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void CloseCommonPopupIfOurs()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var p = Popup.Ins;
|
|
|
|
|
if (p == null || p.m_popup == null || !p.m_popup.activeSelf) return;
|
|
|
|
|
if (p.label_msg == null || !string.Equals(p.label_msg.text, LastMessage)) return;
|
|
|
|
|
p.OnClick_Back();
|
|
|
|
|
CommonPopupClosed++;
|
|
|
|
|
}
|
|
|
|
|
catch { /* 씬 미로드·에디트 모드 — 무동작 */ }
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
// ── 구성 · 배치 (폴백 팝업) ──────────────────────────────────────────
|
|
|
|
|
public bool BuildIfNeeded()
|
|
|
|
|
{
|
|
|
|
|
if (_rt == null) _rt = GetComponent<RectTransform>();
|
|
|
|
|
bool made = false;
|
|
|
|
|
|
|
|
|
|
if (group == null) { group = GetComponent<CanvasGroup>(); if (group == null) { group = gameObject.AddComponent<CanvasGroup>(); made = true; } }
|
|
|
|
|
if (dim == null)
|
|
|
|
|
{
|
|
|
|
|
var d = WLVignetteUtil.NewChild(_rt, "Dim");
|
|
|
|
|
WLVignetteUtil.Stretch(d);
|
|
|
|
|
dim = d.GetComponent<Image>();
|
|
|
|
|
if (dim == null) dim = d.gameObject.AddComponent<Image>();
|
|
|
|
|
dim.raycastTarget = true;
|
|
|
|
|
made = true;
|
|
|
|
|
}
|
|
|
|
|
if (box == null) { box = WLVignetteUtil.NewChild(_rt, "Box"); made = true; }
|
|
|
|
|
if (boxBg == null)
|
|
|
|
|
{
|
|
|
|
|
boxBg = box.GetComponent<Image>();
|
|
|
|
|
if (boxBg == null) { boxBg = box.gameObject.AddComponent<Image>(); made = true; }
|
|
|
|
|
boxBg.raycastTarget = true;
|
|
|
|
|
}
|
|
|
|
|
if (messageLabel == null)
|
|
|
|
|
{
|
|
|
|
|
var t = WLVignetteUtil.NewChild(box, "Msg");
|
|
|
|
|
messageLabel = t.GetComponent<TextMeshProUGUI>();
|
|
|
|
|
if (messageLabel == null) messageLabel = t.gameObject.AddComponent<TextMeshProUGUI>();
|
|
|
|
|
messageLabel.raycastTarget = false;
|
|
|
|
|
messageLabel.alignment = TextAlignmentOptions.Center;
|
|
|
|
|
if (font != null) messageLabel.font = font;
|
|
|
|
|
made = true;
|
|
|
|
|
}
|
|
|
|
|
if (okButton == null)
|
|
|
|
|
{
|
2026-09-09 00:23:06 +00:00
|
|
|
// WL-813z(D-9): 이름이 원본 `NewGameUI(Clone)/…/NightmareResult/btn_ok` 와 겹쳐
|
|
|
|
|
// `LightProbe.ClickButton ["btn_ok"]` 가 비활성인 원본 쪽을 잡았다 → `WL_` 접두어로 분리.
|
|
|
|
|
var t = WLVignetteUtil.NewChild(box, OkButtonName);
|
2026-09-08 17:13:35 +00:00
|
|
|
var img = t.GetComponent<Image>();
|
|
|
|
|
if (img == null) img = t.gameObject.AddComponent<Image>();
|
|
|
|
|
img.raycastTarget = true;
|
|
|
|
|
okButton = t.GetComponent<Button>();
|
|
|
|
|
if (okButton == null) okButton = t.gameObject.AddComponent<Button>();
|
|
|
|
|
okButton.targetGraphic = img;
|
|
|
|
|
okButton.onClick.RemoveListener(OnOkListener);
|
|
|
|
|
okButton.onClick.AddListener(OnOkListener);
|
|
|
|
|
made = true;
|
|
|
|
|
}
|
|
|
|
|
if (okLabel == null)
|
|
|
|
|
{
|
|
|
|
|
var t = WLVignetteUtil.NewChild(okButton.GetComponent<RectTransform>(), "Label");
|
|
|
|
|
okLabel = t.GetComponent<TextMeshProUGUI>();
|
|
|
|
|
if (okLabel == null) okLabel = t.gameObject.AddComponent<TextMeshProUGUI>();
|
|
|
|
|
okLabel.raycastTarget = false;
|
|
|
|
|
okLabel.alignment = TextAlignmentOptions.Center;
|
|
|
|
|
if (font != null) okLabel.font = font;
|
|
|
|
|
made = true;
|
|
|
|
|
}
|
2026-09-10 16:36:55 +00:00
|
|
|
// WL-815d — 두 번째 버튼(스테이지 실패 「로비로」). 평소에는 SetActive(false) 라 813z 화면은 그대로다.
|
|
|
|
|
if (secondaryButton == null)
|
|
|
|
|
{
|
|
|
|
|
var t = WLVignetteUtil.NewChild(box, SecondaryButtonName);
|
|
|
|
|
var img = t.GetComponent<Image>();
|
|
|
|
|
if (img == null) img = t.gameObject.AddComponent<Image>();
|
|
|
|
|
img.raycastTarget = true;
|
|
|
|
|
secondaryButton = t.GetComponent<Button>();
|
|
|
|
|
if (secondaryButton == null) secondaryButton = t.gameObject.AddComponent<Button>();
|
|
|
|
|
secondaryButton.targetGraphic = img;
|
|
|
|
|
secondaryButton.onClick.RemoveListener(OnSecondListener);
|
|
|
|
|
secondaryButton.onClick.AddListener(OnSecondListener);
|
|
|
|
|
t.gameObject.SetActive(false);
|
|
|
|
|
made = true;
|
|
|
|
|
}
|
|
|
|
|
if (secondaryLabel == null)
|
|
|
|
|
{
|
|
|
|
|
var t = WLVignetteUtil.NewChild(secondaryButton.GetComponent<RectTransform>(), "Label");
|
|
|
|
|
secondaryLabel = t.GetComponent<TextMeshProUGUI>();
|
|
|
|
|
if (secondaryLabel == null) secondaryLabel = t.gameObject.AddComponent<TextMeshProUGUI>();
|
|
|
|
|
secondaryLabel.raycastTarget = false;
|
|
|
|
|
secondaryLabel.alignment = TextAlignmentOptions.Center;
|
|
|
|
|
if (font != null) secondaryLabel.font = font;
|
|
|
|
|
made = true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 00:23:06 +00:00
|
|
|
// WL-813z(D-9) — 이전 세션이 만든(또는 직렬화된) `btn_ok` 도 이름을 통일한다.
|
|
|
|
|
if (okButton != null && okButton.gameObject.name != OkButtonName) okButton.gameObject.name = OkButtonName;
|
2026-09-10 16:36:55 +00:00
|
|
|
if (secondaryButton != null && secondaryButton.gameObject.name != SecondaryButtonName) secondaryButton.gameObject.name = SecondaryButtonName;
|
2026-09-08 17:13:35 +00:00
|
|
|
return made;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnOkListener() { OnConfirm(); }
|
2026-09-10 16:36:55 +00:00
|
|
|
private void OnSecondListener() { OnSecondary(); }
|
2026-09-08 17:13:35 +00:00
|
|
|
|
|
|
|
|
public string ApplyLayout()
|
|
|
|
|
{
|
|
|
|
|
if (_rt == null) _rt = GetComponent<RectTransform>();
|
|
|
|
|
var s = WLSurvivalUiSettings.Instance;
|
|
|
|
|
if (s == null) return "WLSurvivalUiSettings 에셋 없음 — 배치 건너뜀";
|
|
|
|
|
if (box == null) return "구성 요소 없음 — BuildIfNeeded 먼저";
|
|
|
|
|
|
|
|
|
|
float u = UnitsPerPx();
|
|
|
|
|
WLVignetteUtil.Stretch(_rt);
|
|
|
|
|
_rt.localScale = Vector3.one;
|
|
|
|
|
|
|
|
|
|
if (dim != null) dim.color = new Color(0f, 0f, 0f, 0.6f);
|
|
|
|
|
|
|
|
|
|
box.anchorMin = new Vector2(0.5f, 0.5f);
|
|
|
|
|
box.anchorMax = new Vector2(0.5f, 0.5f);
|
|
|
|
|
box.pivot = new Vector2(0.5f, 0.5f);
|
|
|
|
|
box.anchoredPosition = Vector2.zero;
|
|
|
|
|
box.sizeDelta = new Vector2(s.reviveFallbackSizePx.x * u, s.reviveFallbackSizePx.y * u);
|
|
|
|
|
if (boxBg != null) boxBg.color = s.reviveFallbackBgColor;
|
|
|
|
|
|
|
|
|
|
if (messageLabel != null)
|
|
|
|
|
{
|
|
|
|
|
var t = messageLabel.rectTransform;
|
|
|
|
|
t.anchorMin = new Vector2(0f, 0.35f); t.anchorMax = new Vector2(1f, 1f);
|
|
|
|
|
t.offsetMin = new Vector2(24f * u, 0f); t.offsetMax = new Vector2(-24f * u, -24f * u);
|
|
|
|
|
messageLabel.fontSize = s.reviveFallbackFontPx * u;
|
|
|
|
|
messageLabel.color = Color.white;
|
|
|
|
|
if (font != null && messageLabel.font != font) messageLabel.font = font;
|
|
|
|
|
}
|
2026-09-10 16:36:55 +00:00
|
|
|
// WL-815d — 버튼 1개(813z 원래) / 2개(스테이지 실패)를 같은 산식으로 앉힌다.
|
|
|
|
|
bool two = StageMode && SecondaryAction != null;
|
|
|
|
|
float btnW = two ? 300f : 320f;
|
|
|
|
|
float halfGap = two ? (btnW + 24f) * 0.5f : 0f;
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
if (okButton != null)
|
|
|
|
|
{
|
|
|
|
|
var t = okButton.GetComponent<RectTransform>();
|
|
|
|
|
t.anchorMin = new Vector2(0.5f, 0f); t.anchorMax = new Vector2(0.5f, 0f);
|
|
|
|
|
t.pivot = new Vector2(0.5f, 0f);
|
2026-09-10 16:36:55 +00:00
|
|
|
t.anchoredPosition = new Vector2(-halfGap * u, 28f * u);
|
|
|
|
|
t.sizeDelta = new Vector2(btnW * u, 96f * u);
|
2026-09-08 17:13:35 +00:00
|
|
|
var img = okButton.targetGraphic as Image;
|
|
|
|
|
if (img != null) img.color = new Color(0.137f, 0.906f, 0.529f, 1f);
|
|
|
|
|
}
|
|
|
|
|
if (okLabel != null)
|
|
|
|
|
{
|
|
|
|
|
WLVignetteUtil.Stretch(okLabel.rectTransform);
|
2026-09-10 16:36:55 +00:00
|
|
|
okLabel.text = two && !string.IsNullOrEmpty(_okOverride) ? _okOverride : s.reviveOkText;
|
2026-09-08 17:13:35 +00:00
|
|
|
okLabel.fontSize = s.reviveFallbackFontPx * u * 0.8f;
|
|
|
|
|
okLabel.color = new Color(0.04f, 0.09f, 0.06f, 1f);
|
|
|
|
|
if (font != null && okLabel.font != font) okLabel.font = font;
|
|
|
|
|
}
|
2026-09-10 16:36:55 +00:00
|
|
|
if (secondaryButton != null)
|
|
|
|
|
{
|
|
|
|
|
if (secondaryButton.gameObject.activeSelf != two) secondaryButton.gameObject.SetActive(two);
|
|
|
|
|
var t = secondaryButton.GetComponent<RectTransform>();
|
|
|
|
|
t.anchorMin = new Vector2(0.5f, 0f); t.anchorMax = new Vector2(0.5f, 0f);
|
|
|
|
|
t.pivot = new Vector2(0.5f, 0f);
|
|
|
|
|
t.anchoredPosition = new Vector2(halfGap * u, 28f * u);
|
|
|
|
|
t.sizeDelta = new Vector2(btnW * u, 96f * u);
|
|
|
|
|
var img = secondaryButton.targetGraphic as Image;
|
|
|
|
|
if (img != null) img.color = new Color(0.16f, 0.18f, 0.22f, 1f);
|
|
|
|
|
}
|
|
|
|
|
if (secondaryLabel != null)
|
|
|
|
|
{
|
|
|
|
|
WLVignetteUtil.Stretch(secondaryLabel.rectTransform);
|
|
|
|
|
secondaryLabel.text = string.IsNullOrEmpty(_secondOverride) ? "" : _secondOverride;
|
|
|
|
|
secondaryLabel.fontSize = s.reviveFallbackFontPx * u * 0.8f;
|
|
|
|
|
secondaryLabel.color = new Color(0.90f, 0.92f, 0.95f, 1f);
|
|
|
|
|
if (font != null && secondaryLabel.font != font) secondaryLabel.font = font;
|
|
|
|
|
}
|
|
|
|
|
return "폴백 팝업 box size" + box.sizeDelta + " 버튼=" + (two ? 2 : 1) +
|
|
|
|
|
" [" + (okLabel != null ? okLabel.text : "") + "]" +
|
|
|
|
|
(two ? " [" + (secondaryLabel != null ? secondaryLabel.text : "") + "]" : "") +
|
|
|
|
|
" unitsPerPx=" + u.ToString("F4");
|
2026-09-08 17:13:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float UnitsPerPx()
|
|
|
|
|
{
|
|
|
|
|
if (_canvas == null) _canvas = GetComponentInParent<Canvas>(true);
|
|
|
|
|
return WLVignetteUtil.UnitsPerPx(_canvas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetFont(TMP_FontAsset f)
|
|
|
|
|
{
|
|
|
|
|
font = f;
|
|
|
|
|
if (f == null) return;
|
|
|
|
|
if (messageLabel != null) messageLabel.font = f;
|
|
|
|
|
if (okLabel != null) okLabel.font = f;
|
2026-09-10 16:36:55 +00:00
|
|
|
if (secondaryLabel != null) secondaryLabel.font = f; // WL-815d
|
2026-09-08 17:13:35 +00:00
|
|
|
}
|
|
|
|
|
|
2026-09-10 16:36:55 +00:00
|
|
|
/// <summary>WL-815d — 지금 화면의 버튼 문구(실측 덤프용).</summary>
|
|
|
|
|
public string PrimaryButtonText { get { return okLabel != null ? okLabel.text : ""; } }
|
|
|
|
|
public string SecondaryButtonText { get { return secondaryLabel != null ? secondaryLabel.text : ""; } }
|
|
|
|
|
public bool SecondaryVisible { get { return secondaryButton != null && secondaryButton.gameObject.activeSelf; } }
|
|
|
|
|
/// <summary>프로브 전용 — 버튼 클릭 경로를 그대로 태운다(리스너와 같은 함수).</summary>
|
|
|
|
|
public string ProbeClickPrimary() { return OnConfirm(); }
|
|
|
|
|
public string ProbeClickSecondary() { return OnSecondary(); }
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
// ── 검증 전용 ────────────────────────────────────────────────────────
|
2026-09-09 00:23:06 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// 프로브 전용(WL-813z) — 에디트 모드에는 공용 `Popup` 싱글턴이 없어 그 경로를 탈 수 없다.
|
|
|
|
|
/// "공용 Popup 으로 열려 있다" 상태만 만들어 탈취 감지·승격 경로를 같은 코드로 검증한다. 런타임 호출 0.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string ProbeSimulateCommonPopup(string msg)
|
|
|
|
|
{
|
|
|
|
|
LastMessage = msg ?? "";
|
|
|
|
|
UsedCommonPopup = true;
|
|
|
|
|
_escalated = false;
|
|
|
|
|
_open = true;
|
|
|
|
|
ShownCount++;
|
|
|
|
|
return "공용 Popup 사용 표시 · open=" + _open + " 본문=\"" + LastMessage.Replace("\n", " / ") + "\"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>프로브 전용(WL-813z) — 탈취 감지 1회를 즉시 돌린다.</summary>
|
|
|
|
|
public string ProbeCheckHijack()
|
|
|
|
|
{
|
|
|
|
|
var g = WLSurvivalSettings.Instance;
|
|
|
|
|
if (g == null) return "WLSurvivalSettings 에셋 없음 — 판정 불가";
|
|
|
|
|
CheckCommonPopupHijack(g);
|
|
|
|
|
return "탈취감지=" + HijackCount + "회 · 승격=" + _escalated + " · 공용팝업사용=" + UsedCommonPopup +
|
|
|
|
|
" · 폴백표시=" + FallbackVisible + " · 맨뒤형제=" + IsLastSibling + " · sortingOrder=" + SortingOrder;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-08 17:13:35 +00:00
|
|
|
public string Dump()
|
|
|
|
|
{
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
var s = WLSurvivalUiSettings.Instance;
|
|
|
|
|
sb.AppendLine("[ReviveDialog] deaths=" + DeathSeen + " shows=" + ShownCount + " confirms=" + ConfirmCount +
|
|
|
|
|
" 대기중=" + Pending + " 공용팝업사용=" + UsedCommonPopup +
|
2026-09-08 22:58:46 +00:00
|
|
|
" 폴백표시=" + FallbackVisible + " open=" + _open +
|
|
|
|
|
" 813i(ReviveRequested)=" + (ReviveRequested != null) +
|
|
|
|
|
" 브리지=" + WLSurvivalUiBridge.Installed);
|
2026-09-08 17:13:35 +00:00
|
|
|
sb.AppendLine(" 본문=\"" + LastMessage.Replace("\n", " / ") + "\"");
|
2026-09-10 16:36:55 +00:00
|
|
|
sb.AppendLine(" [815d] 스테이지모드=" + StageMode + " 버튼=[" + PrimaryButtonText + "]" +
|
|
|
|
|
(SecondaryVisible ? " [" + SecondaryButtonText + "]" : " (두번째 없음)") +
|
|
|
|
|
" · 핸들러 primary=" + (PrimaryOverride != null) + " secondary=" + (SecondaryAction != null) +
|
|
|
|
|
" · 클릭 다시도전=" + StagePrimaryCount + " 로비=" + StageSecondaryCount +
|
|
|
|
|
" · 표시 " + StageShownCount + "회");
|
2026-09-08 17:13:35 +00:00
|
|
|
sb.AppendLine(" 공용 Popup.Ins=" + (Popup.Ins != null ? "있음" : "없음(에디트 모드/씬 미로드)"));
|
2026-09-09 00:23:06 +00:00
|
|
|
// WL-813z(D-3/D-9)
|
|
|
|
|
var g = WLSurvivalSettings.Instance;
|
|
|
|
|
sb.AppendLine(" 최상단 = sortingOrder " + SortingOrder + "(SO " + (g != null ? g.reviveSortingOrder.ToString() : "-") +
|
|
|
|
|
") · 맨뒤형제=" + IsLastSibling + " · 재보장=" + TopmostCount + "회 · 탈취감지=" + HijackCount +
|
|
|
|
|
" · 승격=" + _escalated + " · 공용팝업닫음=" + CommonPopupClosed +
|
|
|
|
|
" · raycaster=" + (_ownRaycaster != null && _ownRaycaster.enabled) +
|
|
|
|
|
" · blocksRaycasts=" + (group != null && group.blocksRaycasts));
|
|
|
|
|
sb.AppendLine(" 워치독 = " + (g == null ? "(WLSurvivalSettings 없음)" :
|
|
|
|
|
g.reviveWatchdogSeconds.ToString("F1") + "s · 무시disable=" + g.reviveWatchdogIgnoresDisable +
|
|
|
|
|
" · 최상단ON=" + g.reviveTopmostEnabled + " 주기=" + g.reviveTopmostIntervalSeconds.ToString("F2") + "s") +
|
|
|
|
|
" · DeathFlow 발동=" + DeathFlow.WatchdogCount + "회 대기=" + DeathFlow.AwaitingSeconds.ToString("F2") + "s");
|
2026-09-08 17:13:35 +00:00
|
|
|
if (box != null)
|
|
|
|
|
sb.AppendLine(" 폴백 box size" + box.sizeDelta + " pos" + box.anchoredPosition +
|
2026-09-09 00:23:06 +00:00
|
|
|
" ok=\"" + (okLabel != null ? okLabel.text : "-") + "\" okName=" +
|
|
|
|
|
(okButton != null ? okButton.gameObject.name : "-"));
|
2026-09-08 17:13:35 +00:00
|
|
|
sb.AppendLine(" 설정 " + (s == null ? "(에셋 없음)" :
|
|
|
|
|
"enabled=" + s.reviveEnabled + " 공용팝업=" + s.reviveUseCommonPopup +
|
|
|
|
|
" delay=" + s.reviveDelaySeconds + "s key=" + s.reviveMessageKey +
|
|
|
|
|
" 비용=\"" + s.reviveCostText + "\""));
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|