Project_WL/Assets/WL/Combat/Survival/WLSurvivalSettings.cs

133 lines
8.3 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// WLSurvivalSettings.cs — "위험과 회복"(무적 해제 게이트 · 받는 피해 배율 · 물약 · 사망/부활) 값의 단일 출처(SOT · C45)
//
// PD 지시 #813 · 발주서 WL-813i §1-4 · 기준서 v1 §B 요소 8 · §D-1 813i 행 · §F-2 (2026-09-09)
//
// 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLSurvivalSettings.asset
// → Resources.Load<WLSurvivalSettings>("WL/WLSurvivalSettings") 로 1회 로드 후 캐시(코어 설정 SO 와 같은 관례).
// 에셋이 없거나 enabled = false 면 Enabled == false → Survival · PotionUse · DeathFlow 가 아무 것도 하지 않는다
// = 원본 + 기존 WL 동작 100%(플레이어 무적 유지 · 물약 없음 · After_Die 3 s 자동 부활). C8 롤백 1순위 스위치.
//
// 🔴 기본 enabled = false — 813j(물약 버튼 · 부활 팝업 UI)와 **동시 병합**할 때 Lead 가 켠다(기준서 §F-2).
// WLGameplaySettings.playerInvincible 은 손대지 않는다 — 이 SO 가 켜지면 Survival.TakeDamage 가 피격 1회 동안만
// 그 플래그를 내렸다가 되돌린다(디스크 값 무변경).
//
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것(PCActor 가 Assembly-CSharp 에 있다).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.Combat.Survival
{
[CreateAssetMenu(fileName = "WLSurvivalSettings", menuName = "WL/Survival Settings", order = 325)]
public sealed class WLSurvivalSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLSurvivalSettings";
static WLSurvivalSettings s_cached;
static bool s_lookupDone;
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null.</summary>
public static WLSurvivalSettings Instance
{
get
{
if (!s_lookupDone)
{
s_cached = Resources.Load<WLSurvivalSettings>(ResourcesPath);
s_lookupDone = true;
}
return s_cached;
}
}
/// <summary>
/// QA·A/B 전용 런타임 스위치(에셋 무수정 · Play 종료 시 도메인 리로드로 자동 0).
/// 0 = 에셋의 enabled 를 따른다 · 1 = 에셋이 꺼져 있어도 켠다(813j 병합 전 QA) · -1 = 강제 off.
/// </summary>
public static int RuntimeOverride;
/// <summary>생존 축이 살아 있는가 = 에셋 존재 · (enabled 또는 강제 on) · 강제 off 아님. false 면 모든 경로가 원본 동작이다.</summary>
public static bool Enabled
{
get
{
var i = Instance;
if (i == null || RuntimeOverride < 0) return false;
return RuntimeOverride > 0 || i.enabled;
}
}
/// <summary>테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다.</summary>
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
// ─────────────────────────────────────────── 전체 스위치
[Header("전체 스위치")]
[Tooltip("끄면 무적 해제 · 물약 · 사망/부활 흐름이 전부 비활성 — 플레이어 무적(WLGameplaySettings.playerInvincible) 그대로 · 원본 After_Die 3 s 자동 부활 그대로. 롤백 1순위. 813j UI 와 동시 병합 때 Lead 가 켠다.")]
public bool enabled = false;
[Tooltip("피해 배율 · 물약 · 사망/부활 결정을 Console 에 남긴다(검증용 · 기본 off · 문자열 GC 발생).")]
public bool verboseLog = false;
// ─────────────────────────────────────────── 받는 피해
[Header("받는 피해 (기준서 §B 요소 8 위험 · §F-2 · 잡몹/보스 분리)")]
[Tooltip("잡몹(Boss 가 아닌 가해자 · 정예 포함)의 피해를 받을지. 끄면 잡몹에게는 원본 무적 그대로.")]
public bool takeMobDamage = true;
[Tooltip("잡몹 피해에 곱하는 받는 피해 배율. 원본 Actor.Get_Damage(방어 공식) 진입 **전** 원 피해에 곱한다. 기준서 제안 0.5 로 시작.")]
public float mobDamageMultiplier = 0.5f;
[Tooltip("보스(eSubRol.Boss 가해자)의 피해를 받을지. 끄면 보스에게는 원본 무적 그대로.")]
public bool takeBossDamage = true;
[Tooltip("보스 피해에 곱하는 받는 피해 배율. 기준서 제안 0.5 로 시작.")]
public float bossDamageMultiplier = 0.5f;
[Tooltip("켜면 정예(eSubRol.Elite) 가해자를 보스 배율·스위치로 다룬다. 기본은 잡몹 취급.")]
public bool eliteCountsAsBoss = false;
// ─────────────────────────────────────────── 물약
[Header("물약 (기준서 §B 요소 8 회복 · 3개/런 · 40 % · 쿨 5 s)")]
[Tooltip("런(맵 진입)당 물약 개수.")]
public int potionCountPerRun = 3;
[Tooltip("1회 회복량(최대 HP 비율 · Actor.Heal(rate)). 0.4 = 40 %.")]
public float potionHealRate = 0.4f;
[Tooltip("사용 후 재사용 대기(초 · 게임 시간 = 일시정지 중 정지).")]
public float potionCooldownSeconds = 5f;
[Tooltip("회복 수치를 원본 데미지 텍스트(HUDDMGUI · 초록)로 띄운다.")]
public bool potionShowHealNumber = true;
[Tooltip("HP 가 가득 차 있으면 사용을 거절한다(개수 낭비 방지 · Actor.Heal 도 만충이면 아무 것도 안 한다).")]
public bool potionRefuseAtFullHp = true;
[Tooltip("Stage/Dungeon 맵에 들어갈 때(맵 ID·게임 모드 변화) 물약 개수를 런 시작값으로 되돌린다. 끄면 PotionUse.ResetRun() 호출(813m 런 시작)로만 리셋된다.")]
public bool potionResetOnMapLoad = true;
// ─────────────────────────────────────────── 사망 · 부활
[Header("사망 · 부활 (기준서 사망 연출 2 s → 부활 요청 → 존 시작점 On_Regen · 부활 후 무적 1.5 s)")]
[Tooltip("사망 연출 길이(초 · unscaled). 이 시간이 지나면 ReviveRequested 를 낸다(813j 팝업).")]
public float deathDisplaySeconds = 2f;
[Tooltip("부활 요청을 낸 뒤 응답까지 Time.timeScale = 0 으로 세계를 세운다(원본 PauseUI 선례). 원본 After_Die(사망 3 s 뒤 자동 부활 · 코루틴 · 게임 시간)를 붙잡아 두는 수단이기도 하다 — 끄면 팝업 응답이 3 s 를 넘길 때 원본이 먼저 부활시킨다(external 부활로 통보).")]
public bool holdWorldWhileReviving = true;
[Tooltip("ReviveRequested 구독자가 0 이면(813j 미병합 · QA) 요청 즉시 자동 부활한다. 끄면 구독자가 없어도 기다린다(autoReviveTimeoutSeconds 참고).")]
public bool autoReviveWhenNoListener = true;
[Tooltip("부활 요청 뒤 이 시간(초 · unscaled) 안에 응답이 없으면 자동 부활. 0 이면 무제한 대기.")]
public float autoReviveTimeoutSeconds = 0f;
[Tooltip("부활 직후 무적 시간(초 · 게임 시간). 이 동안 PC 피격은 원본 무적 경로(피해 0 · Damaged(invincible=true) 발생).")]
public float reviveInvincibleSeconds = 1.5f;
[Tooltip("부활 시 회복 비율(On_Regen healrate · 1 = 전회복 · 원본 After_Die 와 동일).")]
public float reviveHealRate = 1f;
[Tooltip("부활 시 소환수·펫·일꾼도 On_Regen 한다(원본 After_Die 의 PC 분기와 동일).")]
public bool reviveCompanions = true;
}
}