// ─────────────────────────────────────────────────────────────────────────────
// WLSurvivalUiBridge.cs — 813tj 가 열어 둔 6줄을 813i API 에 연결한다 (WL-813y §1-1 · #813)
//
// 813tj 완료보고 §4 ②:
// "813i 병합 시 PotionButton.CountProvider/CooldownRemainProvider/CooldownTotalProvider/UseHandler 4줄 +
// ReviveDialog.NotifyDeath()/ReviveRequested 2줄만 연결하면 끝(UI 수정 0)"
//
// ■ 왜 Gameplay 가 아니라 여기인가
// 813i(`Assets/WL/Combat/Survival/**`)는 **Gameplay 소유**라 이 세션이 손대지 못한다(발주서 §2 금지).
// UI 쪽에서 813i 의 **public API 만 호출**하는 얇은 브리지를 두면 소유 경계를 넘지 않고 6줄이 붙는다.
//
// ■ 연결 6줄 (813i 완료보고 §4 · 전부 `WL.Combat.Survival`)
// ① PotionButton.CountProvider ← PotionUse.Remaining
// ② PotionButton.CooldownRemainProvider ← PotionUse.CooldownRemaining
// ③ PotionButton.CooldownTotalProvider ← PotionUse.CooldownTotal
// ④ PotionButton.UseHandler ← PotionUse.TryUse() == PotionResult.Used
// ⑤ ReviveDialog.NotifyDeath() ← DeathFlow.Died / DeathFlow.ReviveRequested 구독
// ⑥ ReviveDialog.ReviveRequested ← DeathFlow.Revive()
//
// ■ 스위치 = 813i 소유 `WLSurvivalSettings.Enabled`(에셋 `enabled` · **Lead 가 병합 시 켠다**)
// 꺼져 있으면 물약 표시는 813tj 의 표시용 기본값으로 떨어지고, 사망/부활 이벤트는 애초에 안 온다
// → 병합 전후 어느 쪽이든 화면이 깨지지 않는다(C8).
//
// 🔴 어셈블리 주의: Assets/WL/UI/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
// ─────────────────────────────────────────────────────────────────────────────
using System.Text;
using UnityEngine;
using WL.Combat.Survival;
namespace WL.UI
{
public static class WLSurvivalUiBridge
{
public static bool Installed { get; private set; }
// 진단(프로브가 읽는다 · 실측만)
public static int DiedSeen, ReviveRequestedSeen, RevivedSeen, PotionChangedSeen;
public static int ReviveCalls, PotionUseCalls, PotionUseOk;
public static string LastEvent = "";
public static float LastEventTime;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Boot() { Install(); }
/// 6줄을 연결한다(중복 호출 안전). 프로브도 이 경로를 쓴다.
public static string Install()
{
if (Installed) return "이미 연결됨";
// ── 물약 4줄 ──────────────────────────────────────────────────────
// 스위치가 꺼져 있으면 813tj 의 표시용 기본값으로 떨어진다(연결 전 화면과 동일).
PotionButton.CountProvider = ReadCount;
PotionButton.CooldownRemainProvider = ReadCooldownRemain;
PotionButton.CooldownTotalProvider = ReadCooldownTotal;
PotionButton.UseHandler = Use;
// ── 부활 2줄 ──────────────────────────────────────────────────────
ReviveDialog.ReviveRequested = RequestRevive;
DeathFlow.Died.Add(OnDied);
DeathFlow.ReviveRequested.Add(OnReviveRequested);
DeathFlow.Revived.Add(OnRevived);
PotionUse.Changed.Add(OnPotionChanged);
Installed = true;
return "연결 6줄 완료 · survivalEnabled=" + WLSurvivalSettings.Enabled;
}
/// 연결을 끊는다(프로브 종료 · 813tj 원래 상태로 복귀).
public static string Uninstall()
{
if (!Installed) return "연결 안 돼 있음";
PotionButton.ClearProvider();
ReviveDialog.ReviveRequested = null;
DeathFlow.Died.Remove(OnDied);
DeathFlow.ReviveRequested.Remove(OnReviveRequested);
DeathFlow.Revived.Remove(OnRevived);
PotionUse.Changed.Remove(OnPotionChanged);
Installed = false;
return "연결 해제(813tj 미연결 상태로 복귀)";
}
// ── 물약 (813i PotionUse) ─────────────────────────────────────────────
private static int ReadCount()
{
if (!WLSurvivalSettings.Enabled) return FallbackCount();
return PotionUse.Remaining;
}
private static float ReadCooldownRemain()
{
return WLSurvivalSettings.Enabled ? PotionUse.CooldownRemaining : 0f;
}
private static float ReadCooldownTotal()
{
if (!WLSurvivalSettings.Enabled) return FallbackCooldown();
float t = PotionUse.CooldownTotal;
return t > 0f ? t : FallbackCooldown();
}
private static bool Use()
{
PotionUseCalls++;
if (!WLSurvivalSettings.Enabled) { Note("potion:disabled"); return false; }
var r = PotionUse.TryUse();
bool ok = r == PotionResult.Used;
if (ok) PotionUseOk++;
Note("potion:" + r);
return ok;
}
private static int FallbackCount()
{
var s = WLSurvivalUiSettings.Instance;
return s != null ? s.potionFallbackCount : 0;
}
private static float FallbackCooldown()
{
var s = WLSurvivalUiSettings.Instance;
return s != null ? s.potionFallbackCooldownSeconds : 0f;
}
private static void OnPotionChanged(in PotionEvent e)
{
PotionChangedSeen++;
Note("changed:" + e.change + "(" + e.remaining + "/" + e.max + ")");
}
// ── 사망 · 부활 (813i DeathFlow) ──────────────────────────────────────
/// 사망 = 팝업 예약(813tj 는 자체 지연으로 사망 연출 시간을 기다린다).
private static void OnDied(in DeathEvent e)
{
DiedSeen++;
Note("died#" + e.deathCount + " display=" + e.displaySeconds.ToString("F2") + "s · " + ReviveDialog.NotifyDeath());
}
///
/// 813i 가 사망 연출을 끝내고 부활 대기에 들어간 시점 — 지연 없이 지금 연다.
/// **이 구독자가 있어야** 813i 의 `autoReviveWhenNoListener`(구독 0 이면 자동 부활)가 꺼진다.
///
private static void OnReviveRequested(in ReviveRequestEvent e)
{
ReviveRequestedSeen++;
Note("reviveRequested#" + e.deathCount + " worldHeld=" + e.worldHeld + " · " + ReviveDialog.NotifyReviveRequested());
}
private static void OnRevived(in RevivedEvent e)
{
RevivedSeen++;
Note("revived#" + e.deathCount + " external=" + e.external + " auto=" + e.auto + " · " + ReviveDialog.NotifyRevived());
}
/// 팝업 "존 시작점에서 부활" 버튼 → 813i.
private static void RequestRevive()
{
ReviveCalls++;
bool ok = false;
if (WLSurvivalSettings.Enabled) ok = DeathFlow.Revive();
Note("revive() → " + ok);
}
private static void Note(string s)
{
LastEvent = s;
LastEventTime = Time.unscaledTime;
}
// ── 진단 ──────────────────────────────────────────────────────────────
public static void ResetDiagnostics()
{
DiedSeen = ReviveRequestedSeen = RevivedSeen = PotionChangedSeen = 0;
ReviveCalls = PotionUseCalls = PotionUseOk = 0;
LastEvent = ""; LastEventTime = 0f;
}
public static string Dump()
{
var sb = new StringBuilder();
sb.AppendLine("[WLSurvivalUiBridge] installed=" + Installed +
" survivalEnabled=" + WLSurvivalSettings.Enabled +
" · died=" + DiedSeen + " reviveReq=" + ReviveRequestedSeen + " revived=" + RevivedSeen +
" potionChanged=" + PotionChangedSeen +
" reviveCalls=" + ReviveCalls + " potionUse=" + PotionUseOk + "/" + PotionUseCalls +
" last=\"" + LastEvent + "\"");
sb.AppendLine(" 연결 6줄 = count:" + (PotionButton.CountProvider != null) +
" cdRemain:" + (PotionButton.CooldownRemainProvider != null) +
" cdTotal:" + (PotionButton.CooldownTotalProvider != null) +
" use:" + (PotionButton.UseHandler != null) +
" reviveReq:" + (ReviveDialog.ReviveRequested != null) +
" deathFlow구독(Died/ReviveRequested/Revived)=" +
DeathFlow.Died.Count + "/" + DeathFlow.ReviveRequested.Count + "/" + DeathFlow.Revived.Count);
sb.AppendLine(" 813i 현재값 = remaining " + PotionUse.Remaining + "/" + PotionUse.Max +
" cd " + PotionUse.CooldownRemaining.ToString("F2") + "/" + PotionUse.CooldownTotal.ToString("F2") + "s" +
" ready=" + PotionUse.IsReady +
" · DeathFlow state=" + DeathFlow.State + " deaths=" + DeathFlow.DeathCount +
" revives=" + DeathFlow.ReviveCount + " auto=" + DeathFlow.AutoReviveCount);
sb.AppendLine(" UI 가 읽는 값 = count " + ReadCount() + " cdRemain " + ReadCooldownRemain().ToString("F2") +
"s cdTotal " + ReadCooldownTotal().ToString("F2") + "s");
return sb.ToString();
}
}
}