// ───────────────────────────────────────────────────────────────────────────── // WLReactionSettings.cs — 전투 반응(연쇄 처치 · 군집 폭발 · 데미지 합산 윈도우) 값의 단일 출처(SOT · C45) // // PD 지시 #813 · 발주서 WL-811def §1-2 · 설계안 WL-811a §C 811e 행 · 기준서 v1 §B 요소 2 (2026-09-09) // // 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLReactionSettings.asset // → Resources.Load("WL/WLReactionSettings") 로 1회 로드 후 캐시. // 에셋이 없거나 enabled = false 또는 RuntimeDisabled 면 KillChain · DamageAggregator 가 아무 것도 하지 않는다(C8). // HUD 표시는 하지 않는다 — 이 파일의 시스템은 이벤트(KillChain.TierReached · DamageAggregator.Flushed)만 낸다(표시 = 813g U). // ───────────────────────────────────────────────────────────────────────────── using System; using UnityEngine; namespace WL.Combat.Reaction { [Serializable] public struct KillChainTier { [Tooltip("티어 이름(데이터 · HUD 문구는 813g 가 이 이름을 키로 쓴다).")] public string name; [Tooltip("이 티어에 도달하는 연쇄 처치 수.")] public int kills; } [CreateAssetMenu(fileName = "WLReactionSettings", menuName = "WL/Reaction Settings", order = 322)] public sealed class WLReactionSettings : ScriptableObject { public const string ResourcesPath = "WL/WLReactionSettings"; static WLReactionSettings s_cached; static bool s_lookupDone; /// 설정 에셋(1회 로드 후 캐시). 없으면 null. public static WLReactionSettings Instance { get { if (!s_lookupDone) { s_cached = Resources.Load(ResourcesPath); s_lookupDone = true; } return s_cached; } } /// 진단·A/B 전용 런타임 스위치. 에셋을 건드리지 않고 이 반응만 끈다. Play 종료 시 자동 false. public static bool RuntimeDisabled; public static bool Enabled { get { var i = Instance; return i != null && i.enabled && !RuntimeDisabled; } } public static void ClearCache() { s_cached = null; s_lookupDone = false; } // ─────────────────────────────────────────── 전체 스위치 [Header("전체 스위치")] [Tooltip("끄면 연쇄 처치 카운터 · 군집 폭발 · 데미지 합산이 전부 비활성(이벤트 0). 롤백 1순위.")] public bool enabled = true; [Tooltip("티어 도달 · 폭발 · 합산 플러시를 Console 에 남긴다(검증용 · GC 발생).")] public bool verboseLog = false; // ─────────────────────────────────────────── 연쇄 처치 [Header("연쇄 처치 (Killed · 윈도우 안 연속 처치 카운터)")] [Tooltip("연쇄 처치 카운터 사용 여부.")] public bool killChainEnabled = true; [Tooltip("연쇄 유지 시간(초 · unscaled). 마지막 처치로부터 이 시간 안에 다음 처치가 오면 연쇄가 이어진다. 기준서 §B 요소 2 = 3 s.")] public float killChainWindowSeconds = 3f; [Tooltip("메인 PC 가 직접 처치한 것만 센다(killer == 메인 PC). off 면 도트·펫·소환수 처치도 센다.")] public bool killChainRequireMainPCKiller = false; [Tooltip("몬스터 소환수(eSubRol.Summon) 처치도 센다.")] public bool killChainCountSummons = true; [Tooltip("연쇄 위치(무게중심) 계산에 기억하는 최근 처치 수(버퍼 용량 · 첫 사용 때 할당).")] public int killChainBufferSize = 16; [Tooltip("티어 표(오름차순). 연쇄 수가 kills 와 같아지는 순간 TierReached 를 1회 낸다. 기준서 제안 2/3/5/8.")] public KillChainTier[] tiers = new KillChainTier[] { new KillChainTier { name = "DOUBLE", kills = 2 }, new KillChainTier { name = "TRIPLE", kills = 3 }, new KillChainTier { name = "RAMPAGE", kills = 5 }, new KillChainTier { name = "MASSACRE", kills = 8 }, }; // ─────────────────────────────────────────── 군집 폭발 [Header("군집 폭발 (티어 도달 → 처치 무게중심에 VFX 1개 · EffectBudget.ClusterBurst 상한 1)")] [Tooltip("군집 폭발 사용 여부.")] public bool clusterBurstEnabled = true; [Tooltip("폭발을 내는 최소 티어 인덱스(tiers 배열 기준 · 0 = DOUBLE · 1 = TRIPLE).")] public int clusterBurstMinTier = 1; [Tooltip("Assets/Res_Addr/Effect/<이름>.prefab — InGameInfo.Show_Effect 로 풀에서 꺼낸다(에셋 인벤토리 실존 확인 = BoomHit · 대안 FireballHit · Effect_STR_EXTRA_SPLASH).")] public string clusterBurstPrefab = "BoomHit"; [Tooltip("폭발 슬롯(EffectBudget.ClusterBurst)을 잡아 두는 시간(초 · unscaled). 이 시간 안의 다음 폭발은 스킵(동시 ≤ 1).")] public float clusterBurstHoldSeconds = 1f; [Tooltip("무게중심에서 위로 띄우는 높이(m).")] public float clusterBurstHeightOffset = 0.3f; [Tooltip("무게중심이 화면 밖(EffectBudget.IsInView)이면 스킵.")] public bool clusterBurstRequireInView = true; // ─────────────────────────────────────────── 데미지 합산 [Header("데미지 합산 윈도우 (HitConfirmed → 대상별 합산 → Flushed 이벤트 · 표시는 813g)")] [Tooltip("합산 사용 여부.")] public bool damageMergeEnabled = true; [Tooltip("첫 타격부터 이 시간(초 · unscaled) 안의 같은 대상 타격을 하나로 합쳐 Flushed 를 낸다. 기준서 §B 요소 2 = 0.25 s.")] public float damageMergeWindowSeconds = 0.25f; [Tooltip("처치 타격(isKill)은 윈도우를 기다리지 않고 즉시 플러시한다.")] public bool damageMergeFlushOnKill = true; [Tooltip("동시에 합산 중일 수 있는 대상 수(슬롯 · 첫 사용 때 할당). 초과하면 가장 오래된 슬롯을 먼저 플러시한다.")] public int damageMergeSlots = 32; [Tooltip("메인 PC 가 때린 것만 합산한다. off 면 모든 타격(PC 피격 포함)을 대상별로 합산.")] public bool damageMergeMainPCOnly = false; } }