Project_WL/Assets/WL/Combat/Growth/WLGrowthSettings.cs

125 lines
7.1 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// WLGrowthSettings.cs — 성장(레벨업) 연출 값의 단일 출처(SOT · C45)
//
// PD 지시 #813 · 발주서 WL-813k §1-2 (2026-09-09)
//
// 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLGrowthSettings.asset
// → Resources.Load<WLGrowthSettings>("WL/WLGrowthSettings") 1회 로드 후 캐시(WLCombatCoreSettings 관례 그대로).
// 에셋이 없거나 enabled = false 면 Enabled == false → LevelUp 이벤트 자체가 발생하지 않고
// ServerClass.Add_Exp 의 훅 1줄은 즉시 반환한다 = 원본 100%(C8 롤백 1순위 스위치).
// 아래 필드 기본값은 "에셋 부재 시의 폴백"이 아니라 에셋 생성 시 초기값이다.
//
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것(ServerClass/Actor 가 Assembly-CSharp 에 있다).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.Combat.Growth
{
[CreateAssetMenu(fileName = "WLGrowthSettings", menuName = "WL/Growth Settings", order = 321)]
public sealed class WLGrowthSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLGrowthSettings";
static WLGrowthSettings s_cached;
static bool s_lookupDone;
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null.</summary>
public static WLGrowthSettings Instance
{
get
{
if (!s_lookupDone)
{
s_cached = Resources.Load<WLGrowthSettings>(ResourcesPath);
s_lookupDone = true;
}
return s_cached;
}
}
/// <summary>진단·A/B 전용 런타임 스위치(WLCombatCoreSettings.RuntimeDisabled 선례). Play 종료 시 도메인 리로드로 자동 false.</summary>
public static bool RuntimeDisabled;
/// <summary>성장 연출이 살아 있는가 = 에셋 존재 · enabled · 런타임 스위치 off.</summary>
public static bool Enabled
{
get { var i = Instance; return i != null && i.enabled && !RuntimeDisabled; }
}
/// <summary>테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다(에셋 제거/복구 실험).</summary>
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
// ─────────────────────────────────────────── 전체 스위치
[Header("전체 스위치")]
[Tooltip("끄면 LevelUp 이벤트 · 폭발 이펙트 · 전회복 · 스케일 펀치가 전부 비활성 — 원본 Add_Exp 동작 100%. 롤백 1순위.")]
public bool enabled = true;
[Tooltip("레벨업 처리 결과를 Console 에 남긴다(검증용 · 기본 off · GC 가 발생하므로 서비스 빌드에서 켜지 말 것).")]
public bool verboseLog = false;
[Tooltip("코어 CombatEvents.LevelUp(811b 가 API 만 둔 것)에도 같은 레벨업을 전달한다. 코어가 꺼져 있으면 자동으로 무시된다.")]
public bool forwardToCombatEvents = true;
// ─────────────────────────────────────────── 폭발 이펙트
[Header("레벨업 폭발 이펙트 (FX_FireLevelUp · 파티클 9 · 비루프)")]
[Tooltip("스폰할 이펙트 프리팹. 비우면 이펙트를 만들지 않는다(전회복·이벤트는 그대로).")]
public GameObject burstPrefab;
[Tooltip("PC 의 자식으로 붙여 따라다니게 한다. 끄면 발생 위치에 고정된다.")]
public bool burstParentToPc = true;
[Tooltip("PC 발밑 기준 스폰 오프셋(m · 월드 기준).")]
public Vector3 burstOffset = new Vector3(0f, 0.1f, 0f);
[Tooltip("프리팹 스케일 배율(1 = 원본).")]
public float burstScale = 1f;
[Tooltip("이펙트 파괴까지의 수명(초). 0 이면 스폰된 ParticleSystem 들의 duration + startLifetime 최댓값으로 자동 측정한다.")]
public float burstLifetimeSeconds = 0f;
[Tooltip("자동 측정 수명의 상한(초). 루프 파티클이 섞여도 이 시간이면 반드시 파괴된다.")]
public float burstLifetimeMaxSeconds = 6f;
[Tooltip("연속 레벨업(Add_Exp 의 while 루프 · 한 프레임에 여러 번) 때 이펙트를 다시 내는 최소 간격(초 · unscaled). 전회복은 매 레벨 적용된다.")]
public float burstMinIntervalSeconds = 0.5f;
[Tooltip("EffectBudget.Heavy 슬롯을 잡고 스폰한다(상한 초과 시 이펙트만 스킵). 코어가 꺼져 있으면 제한 없음.")]
public bool useEffectBudget = true;
// ─────────────────────────────────────────── HP/MP 전회복
[Header("HP/MP 전회복 (1 = 전회복)")]
[Tooltip("레벨업 시 회복할 최대 HP 비율. 1 = 전회복 · 0 = 회복 없음.")]
[Range(0f, 1f)] public float hpRecoverRate = 1f;
[Tooltip("레벨업 시 회복할 최대 MP 비율. 1 = 전회복 · 0 = 회복 없음.")]
[Range(0f, 1f)] public float mpRecoverRate = 1f;
[Tooltip("회복량을 머리 위 HUD 숫자로 띄운다(원본 Heal/Heal_MP 의 showhud).")]
public bool showRecoverHud = true;
// ─────────────────────────────────────────── 스케일 펀치
[Header("스케일 펀치 (PC · 카메라 미접촉)")]
[Tooltip("PC 스케일 최대 배율. 1 이하면 펀치를 건너뛴다.")]
public float scalePunch = 1.12f;
[Tooltip("스케일 펀치 왕복 시간(초 · unscaled).")]
public float scalePunchDuration = 0.22f;
// ─────────────────────────────────────────── 팝업 스탯 3종
[Header("팝업 스탯 3종 (813g UI 가 페이로드에서 읽는다)")]
[Tooltip("레벨업 팝업 1번 칸 스탯.")]
public eStat statA = eStat.MaxHP;
[Tooltip("레벨업 팝업 2번 칸 스탯.")]
public eStat statB = eStat.MaxMP;
[Tooltip("레벨업 팝업 3번 칸 스탯.")]
public eStat statC = eStat.ATK;
[Tooltip("켜면 레벨업 직후 ActorStatInfo 를 재계산(Reset · HP/MP 유지 · StatChangeUI 미호출)해 페이로드의 new 스탯을 채운다. " +
"원본은 레벨업으로 스탯이 즉시 오르지 않고 스탯 포인트만 주므로 기본 off = 원본 동작(prev == new).")]
public bool refreshStatOnLevelUp = false;
}
}