// ───────────────────────────────────────────────────────────────────────────── // 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(); } } }