2026-09-08 17:06:21 +00:00
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
// WLBossArenaSettings.cs — 보스전 게이트 · 아레나 값의 단일 출처(SOT · C45)
|
|
|
|
|
|
//
|
|
|
|
|
|
// PD 지시 #813 · 발주서 WL-813d §1-4 (2026-09-09)
|
|
|
|
|
|
//
|
|
|
|
|
|
// 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLBossArenaSettings.asset
|
|
|
|
|
|
// → Resources.Load<WLBossArenaSettings>("WL/WLBossArenaSettings") 로 1회 로드 후 캐시
|
|
|
|
|
|
// (WLCombatCoreSettings·WLLootSettings 등 기존 설정 SO 관례 그대로).
|
|
|
|
|
|
//
|
|
|
|
|
|
// ■ C8 롤백 (하나라도 false 면 BossArena 는 아무 일도 하지 않는다 = 813e 상시 배치 100%)
|
|
|
|
|
|
// ① 에셋이 없다 → Active false
|
|
|
|
|
|
// ② enabled = false → Active false
|
|
|
|
|
|
// ③ RuntimeDisabled = true → Active false (진단·A/B 전용 · 도메인 리로드로 자동 해제)
|
|
|
|
|
|
// ④ WLCombatCoreSettings.Enabled = false → Active false
|
|
|
|
|
|
// 🔴 ④ 가 반드시 필요하다: 게이트는 CombatEvents.Killed/Spawned 구독으로만 열린다.
|
|
|
|
|
|
// 코어가 꺼진 채 게이트를 걸면 이벤트가 0 이라 보스가 영원히 안 나온다.
|
|
|
|
|
|
// 그래서 "코어가 꺼지면 게이트도 안 건다"(= 맵 로드 즉시 보스 배치)로 떨어뜨린다.
|
|
|
|
|
|
//
|
|
|
|
|
|
// ■ 아래 필드 기본값은 "SOT 부재 시의 실패 처리"가 아니라 에셋 생성 시 초기값이다.
|
|
|
|
|
|
// 실제로 읽히는 값은 항상 .asset 이며, 코드에는 게임 수치 상수를 두지 않는다(C45).
|
|
|
|
|
|
//
|
|
|
|
|
|
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것(FieldBossData/MobActor 가 Assembly-CSharp 에 있다).
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using WL.Combat.Core;
|
|
|
|
|
|
|
|
|
|
|
|
namespace WL.Combat.Boss
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>보스 등장 게이트 판정 방식.</summary>
|
|
|
|
|
|
public enum eBossGateMode
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>게이트 존이 「동시에」 생존 0 이 되는 순간(전멸).</summary>
|
|
|
|
|
|
ZoneCleared = 0,
|
|
|
|
|
|
/// <summary>관측 존들의 누적 처치 수가 requiredKills 이상.</summary>
|
|
|
|
|
|
KillCount = 1,
|
|
|
|
|
|
/// <summary>둘 중 먼저 만족하는 쪽(권장 — 리젠형 존에서 전멸이 안 잡혀도 열린다).</summary>
|
|
|
|
|
|
Either = 2,
|
|
|
|
|
|
/// <summary>둘 다 만족해야 열린다.</summary>
|
|
|
|
|
|
Both = 3,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[CreateAssetMenu(fileName = "WLBossArenaSettings", menuName = "WL/Boss Arena Settings", order = 326)]
|
|
|
|
|
|
public sealed class WLBossArenaSettings : ScriptableObject
|
|
|
|
|
|
{
|
|
|
|
|
|
public const string ResourcesPath = "WL/WLBossArenaSettings";
|
|
|
|
|
|
|
|
|
|
|
|
static WLBossArenaSettings s_cached;
|
|
|
|
|
|
static bool s_lookupDone;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null.</summary>
|
|
|
|
|
|
public static WLBossArenaSettings Instance
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!s_lookupDone)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_cached = Resources.Load<WLBossArenaSettings>(ResourcesPath);
|
|
|
|
|
|
s_lookupDone = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return s_cached;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>진단·A/B 전용 런타임 스위치. 에셋을 건드리지 않고 게이트만 끈다(도메인 리로드로 자동 false).</summary>
|
|
|
|
|
|
public static bool RuntimeDisabled;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>게이트가 살아 있는가 = 에셋 존재 · enabled · 런타임 스위치 off · 코어 on. false 면 813e 상시 배치 그대로.</summary>
|
|
|
|
|
|
public static bool Active
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
var i = Instance;
|
|
|
|
|
|
return i != null && i.enabled && !RuntimeDisabled && WLCombatCoreSettings.Enabled;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다(에셋 제거/복구 실험).</summary>
|
|
|
|
|
|
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
|
|
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 전체 스위치
|
|
|
|
|
|
[Header("전체 스위치")]
|
|
|
|
|
|
[Tooltip("끄면 보스 등장 게이트·아레나 경계·처치 후 정리가 전부 비활성 — 813e 의 「맵 로드 즉시 상시 배치」 100%. 롤백 1순위.")]
|
|
|
|
|
|
public bool enabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("게이트 판정·리셋·정리를 Console 에 남긴다(검증용 · 기본 off).")]
|
|
|
|
|
|
public bool verboseLog = false;
|
|
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 대상 보스
|
|
|
|
|
|
[Header("대상 보스 (WL_Nature · Anubis)")]
|
|
|
|
|
|
[Tooltip("게이트를 걸 FieldBossData 의 n_SpawnerId. 다른 값의 FieldBossData 는 손대지 않는다(원본 즉시 배치). 0 이면 모든 FieldBossData 가 대상.")]
|
|
|
|
|
|
public int bossSpawnerId = 813010;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스의 MonsterList n_MonsterID. 추적 반경 데이터화 대상 행이다(0 이면 반경을 건드리지 않는다).")]
|
|
|
|
|
|
public int bossMonsterId = 10006;
|
|
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 등장 게이트
|
|
|
|
|
|
[Header("등장 게이트 (존 전멸 → 보스 등장)")]
|
|
|
|
|
|
[Tooltip("끄면 게이트를 걸지 않는다 — 보스는 맵 로드 즉시 등장(813e 그대로). 아레나 경계·정리만 쓰고 싶을 때.")]
|
|
|
|
|
|
public bool gateEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("생존/처치를 세는 존 SpawnerID 전체. 누적 처치(KillCount) 판정의 모수다.")]
|
|
|
|
|
|
public int[] zoneSpawnerIds = new int[] { 813001, 813002, 813003, 813004, 813005 };
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("「전멸」을 봐야 하는 존 SpawnerID. 이 존들이 동시에 생존 0 이 되면 게이트가 열린다(ZoneCleared).")]
|
|
|
|
|
|
public int[] gateZoneSpawnerIds = new int[] { 813004, 813005 };
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("판정 방식. 813e 존은 리젠형(Random/Order)이라 전멸이 순간적이다 → Either 권장.")]
|
|
|
|
|
|
public eBossGateMode gateMode = eBossGateMode.Either;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("KillCount 방식의 임계 — zoneSpawnerIds 누적 처치 수. 813e 존 동시 생존 합 = 12+15+14+8+5 = 54.")]
|
|
|
|
|
|
public int requiredKills = 54;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("true 면 「한 번도 스폰된 적 없는 존」은 전멸로 치지 않는다(맵 로드 직후 alive 0 오판 방지).")]
|
|
|
|
|
|
public bool requireZoneSpawnedOnce = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("조건 충족 후 실제 등장까지의 지연(초 · unscaled). 0 이면 같은 프레임에 등장. 컷인·배너를 앞세우려면 여기서 벌린다.")]
|
|
|
|
|
|
public float gateOpenDelaySeconds = 0f;
|
|
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 아레나
|
|
|
|
|
|
[Header("아레나 경계 (813e 실측 중심 (139, 0.8, −20) · 반경 20)")]
|
|
|
|
|
|
[Tooltip("끄면 추적 반경 데이터화·리쉬·리셋을 하지 않는다(게이트만 쓴다).")]
|
|
|
|
|
|
public bool arenaEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("true 면 아레나 중심 = FieldBossData 의 월드 좌표(813e 가 심은 그 자리). false 면 arenaCenter 를 쓴다.")]
|
|
|
|
|
|
public bool useFieldBossPositionAsCenter = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("useFieldBossPositionAsCenter = false 일 때의 아레나 중심(월드).")]
|
|
|
|
|
|
public Vector3 arenaCenter = new Vector3(139f, 0.8f, -20f);
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("아레나 반경(m · XZ 평면). 813e 실측: 최근접 스포너까지 20.1 m.")]
|
|
|
|
|
|
public float arenaRadius = 20f;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스 등장 직전 MonsterList 행(bossMonsterId)의 f_BaseChaseRange 에 넣을 값. 0 이하면 테이블 값 그대로(원본 50).")]
|
|
|
|
|
|
public float bossChaseRange = 20f;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스 처치 후 정리 때 f_BaseChaseRange 를 원래 값으로 되돌린다(같은 세션에서 다른 맵이 같은 몬스터를 쓸 때 대비).")]
|
|
|
|
|
|
public bool restoreChaseRangeOnCleanup = true;
|
|
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 리쉬 · 리셋
|
|
|
|
|
|
[Header("리쉬 · 리셋 (아레나 이탈)")]
|
|
|
|
|
|
[Tooltip("끄면 보스가 아레나 밖으로 나가도 되돌리지 않는다(테이블 반경만으로 제어).")]
|
|
|
|
|
|
public bool leashEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("경계 검사 주기(초 · unscaled). 0 이하면 매 프레임.")]
|
|
|
|
|
|
public float leashCheckIntervalSeconds = 0.25f;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스가 아레나 중심에서 (반경 + 이 값) 을 넘으면 복귀시킨다.")]
|
|
|
|
|
|
public float leashMargin = 2f;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("PC 가 아레나 밖으로 나가면 보스를 리셋한다(발주서 §1-2 「SO 옵션」).")]
|
|
|
|
|
|
public bool resetWhenPlayerLeavesArena = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("PC 이탈 판정 여유 — PC 가 (반경 + 이 값) 밖일 때만 이탈로 본다.")]
|
|
|
|
|
|
public float playerLeaveMargin = 4f;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("PC 이탈이 이 시간(초 · unscaled) 이상 이어져야 리셋한다. 경계에서 왔다 갔다 할 때 리셋이 튀는 것을 막는다.")]
|
|
|
|
|
|
public float playerLeaveGraceSeconds = 1.5f;
|
|
|
|
|
|
|
2026-09-09 01:18:35 +00:00
|
|
|
|
[Tooltip("리셋 때 보스를 회복시킨다. 끄면 워프·타깃 해제만 한다(회복 0).")]
|
2026-09-08 17:06:21 +00:00
|
|
|
|
public bool resetHealsBoss = true;
|
|
|
|
|
|
|
2026-09-09 01:18:35 +00:00
|
|
|
|
[Tooltip("만피 리셋(On_Regen) 회복 비율(1 = 풀피). leashHealPercent >= 1 일 때만 쓰인다.")]
|
2026-09-08 17:06:21 +00:00
|
|
|
|
public float resetHealRate = 1f;
|
|
|
|
|
|
|
2026-09-09 01:18:35 +00:00
|
|
|
|
// ── WL-813w §1-3 (Q4b D-11): 리쉬 1회당 만피 복구가 69회 = 보스 HP 가 절대 안 준다 → 부분 회복으로 완화
|
|
|
|
|
|
[Tooltip("리쉬 리셋 때의 회복량 = 최대 HP 대비 비율(Actor.Heal(rate) · 누적 회복). "
|
2026-09-09 04:22:50 +00:00
|
|
|
|
+ "🔴 1 이상이면 813d 원래 동작(On_Regen 만피 리셋 = C8 롤백). 0 이면 회복 없이 워프만. "
|
|
|
|
|
|
+ "🔴 813w2 §1-4: Q5 실측(런당 리셋 15~19회 · 회복 8~9회)에서 0.15 는 깎은 HP 를 되돌렸다 → 0.05.")]
|
|
|
|
|
|
public float leashHealPercent = 0.05f;
|
2026-09-09 01:18:35 +00:00
|
|
|
|
|
|
|
|
|
|
[Tooltip("부분 회복의 최소 간격(초 · unscaled). 이 안에 리쉬가 다시 걸리면 워프·타깃 해제만 하고 회복은 건너뛴다(최대 1회/10 s).")]
|
|
|
|
|
|
public float leashHealCooldownSeconds = 10f;
|
|
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 보스 이름 (D-12)
|
|
|
|
|
|
[Header("보스 이름 (D-12 · HP 바 라벨)")]
|
|
|
|
|
|
[Tooltip("보스 HP 바 라벨을 MonsterList.n_MonsterName(테이블 이름 · 813tj 배너와 같은 경로)으로 쓴다. "
|
|
|
|
|
|
+ "끄면 원본 gameObject.name 그대로(= 「Anubis(Clone)」). "
|
|
|
|
|
|
+ "🔴 발주서 §2 가 UI 수정을 BossHpBar.cs 1파일로 묶어 UI 설정 SO 에 필드를 넣을 수 없어 여기 둔다(bossMonsterId 폴백과 같은 자리).")]
|
|
|
|
|
|
public bool bossNameFromTable = true;
|
|
|
|
|
|
|
2026-09-08 17:06:21 +00:00
|
|
|
|
// ─────────────────────────────────────────── 처치 후 정리
|
|
|
|
|
|
[Header("처치 후 정리")]
|
|
|
|
|
|
[Tooltip("보스 처치 후 FieldBossData 의 RegenTime 재등장 코루틴을 끊는다(발주서 §1-3 「리젠 없음」).")]
|
|
|
|
|
|
public bool suppressRegenAfterBossKill = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("처치 → 정리까지의 지연(초 · unscaled). 킬캠(811d)·폭발 드랍(813f)·디졸브가 끝난 뒤에 손대려고 벌린다. RegenTime(60) 보다 작아야 한다.")]
|
|
|
|
|
|
public float cleanupDelaySeconds = 3f;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("정리 때 아레나 존 스포너(zoneSpawnerIds)를 비활성화한다. 기본 off — 켜면 남은 잡몹이 자식째 사라진다.")]
|
|
|
|
|
|
public bool deactivateZonesAfterBossKill = false;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("정리 때 게이트를 다시 잠근다(존을 다시 밀면 보스 재등장). 기본 off = 1회성.")]
|
|
|
|
|
|
public bool rearmGateAfterBossKill = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|