// ───────────────────────────────────────────────────────────────────────────── // WLSurvivalUiSettings.cs — 생존 UI(물약 버튼 · 부활 팝업 · 피격 테두리) 값의 단일 출처 // (WL-813tj · 813j · #813) // // 기준서 v1 §D-1 813j 행: "813c 의 빈 자리에 물약 버튼(잔량 · 쿨 링) · 사망 시 부활 팝업 // (공용 팝업 SortOrder_5 재사용 · '존 시작점에서 부활' · 비용 항목은 PD BM 전까지 무료) // · HP 바 붉은 테두리(피격 0.3 s · UI 이미지 · 포스트 금지)" // §B 요소 8: 물약 3개/런 · 40 % 회복 · 쿨 5 s · 사망 연출 2 s → 부활(존 시작점). // // ■ 데이터의 주인 (🔴 중요) // 물약 개수·회복률·쿨·사망 연출 시간의 **진짜 주인은 813i(Gameplay)의 WLSurvivalSettings** 다. // 이 파일의 같은 이름 값은 **813i 가 아직 없을 때 UI 가 무엇을 그릴지**를 정하는 표시용 기본값이다 // (813i 병합 후에는 아래 Provider 델리게이트가 채워지고 이 기본값은 쓰이지 않는다 — PotionButton 참조). // // ■ px → 캔버스 유닛 // 813c 의 WLHudLayoutSettings.UnitsPerPx(canvas) 하나만 쓴다(산식 중복 0). // 물약 버튼 좌표도 여기 두지 않는다 — 813c 의 예약 슬롯(ReserveSlotPx)이 자리의 주인이다. // // 🔴 어셈블리 주의: Assets/WL/UI/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일). // ───────────────────────────────────────────────────────────────────────────── using UnityEngine; namespace WL.UI { [CreateAssetMenu(fileName = "WLSurvivalUiSettings", menuName = "WL/Survival UI Settings", order = 32)] public sealed class WLSurvivalUiSettings : ScriptableObject { /// Resources 경로 — 런타임 로드 키. public const string ResourcesPath = "WL/WLSurvivalUiSettings"; private static WLSurvivalUiSettings s_instance; private static bool s_tried; /// 에셋이 없으면 null(호출부는 null 이면 전부 건너뛴다 = C8 롤백). public static WLSurvivalUiSettings Instance { get { if (s_instance == null && !s_tried) { s_tried = true; s_instance = Resources.Load(ResourcesPath); } return s_instance; } } public static void ClearCache() { s_instance = null; s_tried = false; } // ── 물약 버튼 ───────────────────────────────────────────────────────── [Header("물약 버튼 (813c 예약 슬롯 자리)")] [Tooltip("끄면 버튼을 만들지도 배치하지도 않는다(원본 100% · C8 롤백)")] public bool potionButtonEnabled = true; [Tooltip("813c 예약 슬롯 인덱스 — 0 = 물약 자리, 1 = 회피 자리(813r)")] public int potionReserveSlotIndex = 0; [Tooltip("아이콘 스프라이트가 붙기 전까지 쓸 글자(아트 확정 시 스프라이트로 교체)")] public string potionLabelText = "물약"; [Tooltip("아이콘 글자 크기 (px)")] public float potionLabelFontPx = 30f; [Tooltip("잔량 표기 형식 — {0} = 남은 개수")] public string potionCountFormat = "{0}"; [Tooltip("잔량 글자 크기 (px)")] public float potionCountFontPx = 34f; [Tooltip("버튼 바탕 색")] public Color potionBgColor = new Color(0.129f, 0.145f, 0.176f, 0.85f); [Tooltip("사용 가능할 때 테두리·글자 색")] public Color potionReadyColor = new Color(0.137f, 0.906f, 0.529f, 1f); // #23E787 [Tooltip("잔량 0 또는 쿨 중일 때 색")] public Color potionDimColor = new Color(0.651f, 0.651f, 0.655f, 1f); // #A6A6A7 [Tooltip("쿨 링 색 — 원본 SkillCard 의 i_cooltime 과 같은 계열")] public Color potionCooldownColor = new Color(0f, 0f, 0f, 0.65f); [Header("물약 표시용 기본값 (🔴 진짜 값의 주인은 813i · WLSurvivalSettings)")] [Tooltip("813i 가 없을 때 표시할 잔량 — 기준서 §B 요소 8 = 3개/런")] public int potionFallbackCount = 3; [Tooltip("813i 가 없을 때 표시할 쿨 길이(초) — 기준서 §B 요소 8 = 5 s")] public float potionFallbackCooldownSeconds = 5f; [Tooltip("813i Provider 가 연결되기 전에는 버튼을 눌러도 아무 일도 하지 않는다(오동작 방지)")] public bool potionRequireProvider = true; // ── 부활 팝업 ───────────────────────────────────────────────────────── [Header("부활 팝업 (공용 팝업 Popup/SortOrder_5 재사용)")] [Tooltip("끄면 사망 통지를 받아도 팝업을 띄우지 않는다")] public bool reviveEnabled = true; [Tooltip("공용 Popup 싱글턴을 재사용한다(끄면 자체 노드로 폴백 — 공용 팝업이 없는 씬 대비)")] public bool reviveUseCommonPopup = true; [Tooltip("사망 통지 → 팝업까지 지연(초) — 기준서 §B 요소 8 사망 연출 2 s. 연출 자체는 813i 소유")] public float reviveDelaySeconds = 2f; [Tooltip("팝업 본문 — localtext 키가 없어 문구를 값으로 둔다(로컬라이즈되면 reviveMessageKey 사용)")] [TextArea(1, 3)] public string reviveMessage = "존 시작점에서 부활합니다."; [Tooltip("0 보다 크면 이 localtext 키를 우선 사용한다(로컬라이즈 이후)")] public int reviveMessageKey = 0; [Tooltip("비용 표기 — PD BM 결정 전까지 무료(기준서 §D-1 813j)")] public string reviveCostText = "무료"; [Tooltip("비용 줄을 본문 아래에 덧붙인다")] public bool reviveShowCostLine = true; [Tooltip("폴백 팝업(공용 팝업이 없을 때)의 확인 버튼 글자")] public string reviveOkText = "부활"; [Tooltip("폴백 팝업 크기 (px)")] public Vector2 reviveFallbackSizePx = new Vector2(760f, 380f); [Tooltip("폴백 팝업 뒷판 색")] public Color reviveFallbackBgColor = new Color(0.06f, 0.07f, 0.09f, 0.92f); [Tooltip("폴백 팝업 글자 크기 (px)")] public float reviveFallbackFontPx = 44f; // ── 피격 테두리 · 비네트 ────────────────────────────────────────────── [Header("피격 연출 (Damaged 구독 · UI 이미지 · 포스트 금지)")] [Tooltip("끄면 Damaged 를 구독하지 않는다")] public bool hitFeedbackEnabled = true; [Tooltip("🔴 무적 피격(playerInvincible 1)은 무시한다 — Q1 QA 실측 2045회 전부 dmg=0 이었다")] public bool ignoreInvincibleHits = true; [Tooltip("연속 피격 스로틀(초) — 자동전투 다단 히트에서 깜빡임 폭주를 막는다")] public float hitMinIntervalSeconds = 0.15f; [Tooltip("HP 바 붉은 테두리를 그린다 — 기준서 §D-1 813j")] public bool hitBorderEnabled = true; [Tooltip("테두리 유지 시간(초) — 기준서 0.3")] public float hitBorderSeconds = 0.3f; [Tooltip("테두리 두께 (px)")] public float hitBorderThicknessPx = 8f; [Tooltip("테두리 색")] public Color hitBorderColor = new Color(1f, 0.271f, 0.278f, 0.95f); // #FD4547 [Tooltip("붉은 테두리를 두를 대상 경로(NewGameUI 루트 기준) — 실측 Common/MyInfoUI/Slider_hp (295×12 px). " + "Safe Area 재부모화 뒤에도 WL_SafeArea_* 를 건너뛰고 찾는다")] public string hitBorderTargetPath = "Common/MyInfoUI/Slider_hp"; [Tooltip("화면 가장자리 피격 비네트도 함께 번쩍인다")] public bool hitVignetteEnabled = true; [Tooltip("피격 비네트 유지 시간(초)")] public float hitVignetteSeconds = 0.3f; [Tooltip("피격 비네트 두께 (px)")] public float hitVignetteThicknessPx = 140f; [Tooltip("피격 비네트 색(알파 = 최고 밝기) — 경고 비네트보다 옅게")] public Color hitVignetteColor = new Color(1f, 0.165f, 0.165f, 0.35f); // ── 진단 ────────────────────────────────────────────────────────────── [Header("진단")] [Tooltip("피격·물약·부활 전이를 콘솔에 남긴다(기본 꺼짐)")] public bool verboseLog = false; } }