2026-09-09 01:18:35 +00:00
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
// WLRunGuideSettings.cs — 런 진행 가이드(존 이동 · 보스 우선) 값의 단일 출처(SOT · C45)
|
|
|
|
|
//
|
|
|
|
|
// PD 지시 #813 · 발주서 WL-813w §1 (2026-09-09) · 근거 = qa/W2-4b_재채점_v2.md D-11 · D-13
|
|
|
|
|
//
|
|
|
|
|
// 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLRunGuideSettings.asset
|
|
|
|
|
// → Resources.Load<WLRunGuideSettings>("WL/WLRunGuideSettings") 로 1회 로드 후 캐시
|
|
|
|
|
// (WLRunSettings · WLBossArenaSettings 관례 그대로).
|
|
|
|
|
//
|
|
|
|
|
// ■ C8 롤백 (하나라도 false 면 RunGuide 는 아무 일도 하지 않는다 = 원본 자동전투 100%)
|
|
|
|
|
// ① 에셋이 없다 → Enabled false
|
|
|
|
|
// ② enabled = false → Enabled false (러너 자체가 뜨지 않는다 · 구독 0)
|
|
|
|
|
// ③ RuntimeDisabled = true → Enabled false (진단·A/B 전용 · 도메인 리로드로 자동 해제)
|
|
|
|
|
// ④ WLRunSettings.Enabled = false → Enabled false
|
|
|
|
|
// 🔴 ④ 가 필요한 이유: 가이드의 목적지는 RunEvents(존 클리어·게이트·보스)로만 정해진다.
|
|
|
|
|
// 런 축이 꺼지면 이벤트가 0 이라 가이드가 목적지를 영원히 못 받는다 → 아예 붙지 않는다.
|
|
|
|
|
//
|
|
|
|
|
// ■ 아래 필드 기본값은 에셋 생성 시 초기값이다. 실제로 읽히는 값은 항상 .asset 이며
|
|
|
|
|
// 코드에는 게임 수치 상수를 두지 않는다(C45).
|
|
|
|
|
//
|
|
|
|
|
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것(PCActor 가 Assembly-CSharp 에 있다).
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace WL.Combat.Run
|
|
|
|
|
{
|
|
|
|
|
[CreateAssetMenu(fileName = "WLRunGuideSettings", menuName = "WL/Run Guide Settings", order = 328)]
|
|
|
|
|
public sealed class WLRunGuideSettings : ScriptableObject
|
|
|
|
|
{
|
|
|
|
|
public const string ResourcesPath = "WL/WLRunGuideSettings";
|
|
|
|
|
|
|
|
|
|
static WLRunGuideSettings s_cached;
|
|
|
|
|
static bool s_lookupDone;
|
|
|
|
|
|
|
|
|
|
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null.</summary>
|
|
|
|
|
public static WLRunGuideSettings Instance
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (!s_lookupDone)
|
|
|
|
|
{
|
|
|
|
|
s_cached = Resources.Load<WLRunGuideSettings>(ResourcesPath);
|
|
|
|
|
s_lookupDone = true;
|
|
|
|
|
}
|
|
|
|
|
return s_cached;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>진단·A/B 전용 런타임 스위치(도메인 리로드로 자동 false).</summary>
|
|
|
|
|
public static bool RuntimeDisabled;
|
|
|
|
|
|
|
|
|
|
/// <summary>가이드가 살아 있는가 = 에셋 존재 · enabled · 런타임 스위치 off · 런 축 on.</summary>
|
|
|
|
|
public static bool Enabled
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var i = Instance;
|
|
|
|
|
return i != null && i.enabled && !RuntimeDisabled && WLRunSettings.Enabled;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다.</summary>
|
|
|
|
|
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 전체 스위치
|
|
|
|
|
[Header("전체 스위치")]
|
|
|
|
|
[Tooltip("끄면 존 이동 가이드·보스 우선 타깃이 전부 비활성 — 원본 자동전투 100%(Q4b 상태 그대로). 롤백 1순위.")]
|
|
|
|
|
public bool enabled = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("가이드 상태 전이·목적지·타깃 무시를 Console 에 남긴다(검증용 · 기본 off).")]
|
|
|
|
|
public bool verboseLog = false;
|
|
|
|
|
|
|
|
|
|
[Tooltip("가이드 폴링 주기(초 · unscaled). 0.02 미만은 0.02 로 올린다.")]
|
|
|
|
|
public float pollSeconds = 0.1f;
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 존 이동
|
|
|
|
|
[Header("존 이동 (D-13 · 존 클리어 → 다음 존)")]
|
|
|
|
|
[Tooltip("끄면 존 이동 가이드를 하지 않는다(보스 가이드만 쓴다).")]
|
|
|
|
|
public bool zoneGuideEnabled = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("런 시작(RunStarted) 때도 첫 존으로 유도한다. 기본 off — Q4b 실측상 PC 는 존1 에서 이미 교전한다.")]
|
|
|
|
|
public bool guideOnRunStart = false;
|
|
|
|
|
|
|
|
|
|
[Tooltip("존 앵커에 이 거리(m · XZ) 안으로 들어오면 「도착」 = 조종을 놓고 원본 자동전투에 넘긴다.")]
|
2026-09-09 04:22:50 +00:00
|
|
|
public float zoneArriveRadius = 6f;
|
2026-09-09 01:18:35 +00:00
|
|
|
|
|
|
|
|
[Tooltip("도착 뒤에도 PC 가 앵커에서 이 거리(m) 밖으로 밀려나면 다시 끌어온다(존1 로 되돌아가는 것을 막는다). 0 이하면 유지 안 함.")]
|
|
|
|
|
public float zoneHoldRadius = 18f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("스포너 노드를 못 찾았을 때 쓸 존 앵커(월드 좌표). 순서 = WLRunSettings.zoneSpawnerIds. "
|
|
|
|
|
+ "런타임에는 언제나 MobControlMgr.SpawnerID 가 같은 노드의 실좌표를 먼저 쓴다.")]
|
|
|
|
|
public Vector3[] zoneAnchorFallbacks = new Vector3[]
|
|
|
|
|
{
|
|
|
|
|
new Vector3(115f, 0.39f, -22f), // 813001
|
|
|
|
|
new Vector3(147f, 2.59f, 30f), // 813002
|
|
|
|
|
new Vector3( 71f, -0.29f, 18f), // 813003
|
|
|
|
|
new Vector3(159f, 0.88f, -22f), // 813004
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 공통 조종 규칙
|
|
|
|
|
[Header("조종 규칙 (원본 타깃/이동 API 만 쓴다)")]
|
|
|
|
|
[Tooltip("가이드 중 PC 의 타깃이 목적지(앵커/아레나 중심)에서 이 거리(m) 밖이면 무시한다(Del_Target). "
|
2026-09-09 04:22:50 +00:00
|
|
|
+ "이 안의 적은 원본 자동전투가 그대로 잡는다. "
|
|
|
|
|
+ "🔴 813w2: zoneHoldRadius(18) 보다 작게 두되 너무 작으면 존 안의 적을 스스로 버려 화면 내 적이 줄어든다(D-19).")]
|
|
|
|
|
public float guideLeashRadius = 14f;
|
2026-09-09 01:18:35 +00:00
|
|
|
|
|
|
|
|
[Tooltip("조종 중 NavMeshAgent.stoppingDistance(m). 목적지 코앞에서 멈춰 서서 도착 판정이 안 나는 것을 막는다.")]
|
|
|
|
|
public float guideStopDistance = 0.5f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("목적지 재지정(SetDestination) 주기(초). 매 폴링마다 부르지 않게 벌린다. 0 이면 매 폴링.")]
|
|
|
|
|
public float repathIntervalSeconds = 0.5f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("PC 가 맞으면 이 시간(초) 동안 가이드를 멈추고 즉시 응전한다(원본 자동전투가 잡는다). 0 이면 응전 중단 없음.")]
|
|
|
|
|
public float resumeAfterHitSeconds = 3f;
|
|
|
|
|
|
2026-09-09 04:22:50 +00:00
|
|
|
[Tooltip("한 목적지에 이 시간(초) 안에 도착 못 하면 가이드를 포기한다(경로 불가·지형 끼임 보호). 0 이하면 무제한. "
|
|
|
|
|
+ "포기해도 상태는 Hold 로 떨어져 이탈 감시(되돌림)는 계속한다.")]
|
2026-09-09 01:18:35 +00:00
|
|
|
public float maxTravelSeconds = 60f;
|
|
|
|
|
|
2026-09-09 04:22:50 +00:00
|
|
|
// ─────────────────────────────────────────── 813w2 §1-1 · §1-2 (D-24 · D-22 공통 원인)
|
|
|
|
|
[Header("피격 응전 범위 (813w2 · D-22/D-24 공통 원인)")]
|
|
|
|
|
[Tooltip("🔴 813w 는 피격 중단이 「도착 판정·이탈 되돌림」까지 통째로 막았다(RunGuide.cs:291). "
|
|
|
|
|
+ "켜면 피격 중단은 Hold(= 목적지 반경 안)에서만 걸고, 이동·되돌림 중에는 무시한다. "
|
|
|
|
|
+ "끄면 813w 그대로(C8 롤백 · Q5 실측 = 존 Arrived 0 · 아레나 이탈 46 m).")]
|
|
|
|
|
public bool hitSuspendOnlyInHold = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스 Hold 에서의 피격 중단(초). 존은 resumeAfterHitSeconds 를 쓴다. "
|
|
|
|
|
+ "0 이면 보스전에서는 피격 중단 없음(되돌림·보스 타깃이 항상 우선).")]
|
|
|
|
|
public float bossHitSuspendSeconds = 0.5f;
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 813w2 §1-1 도착 판정(정지 판정)
|
|
|
|
|
[Header("도착 판정 보강 (813w2 · D-24 · Q5 §4-2 「존 Arrived=0」)")]
|
|
|
|
|
[Tooltip("도착 반경 안으로 못 들어가도, 조종 중 PC 가 사실상 멈춰 서면 도착으로 확정한다 "
|
|
|
|
|
+ "(NavMesh 높이·stoppingDistance·지형 때문에 반경 안으로 못 붙는 경우). 끄면 반경 판정만.")]
|
|
|
|
|
public bool arriveStillEnabled = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("「멈춰 섰다」로 보는 시간(초 · unscaled). 이 시간 내내 arriveStillSpeed 미만이면 도착.")]
|
|
|
|
|
public float arriveStillSeconds = 0.5f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("「멈춰 섰다」로 보는 XZ 속도(m/s). 이 값 미만이면 정지로 센다.")]
|
|
|
|
|
public float arriveStillSpeed = 0.3f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("정지 판정으로 도착을 확정할 수 있는 최대 거리(m). 목적지 반경(holdRadius) 안일 때만 인정한다 — "
|
|
|
|
|
+ "0 이면 holdRadius 를 그대로 쓴다. 훨씬 멀리서 끼인 경우는 maxTravelSeconds 가 처리한다.")]
|
|
|
|
|
public float arriveStillMaxDistance = 0f;
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 813w2 §1-2 되돌림
|
|
|
|
|
[Header("되돌림 (813w2 · D-22 · 이탈 46 m)")]
|
|
|
|
|
[Tooltip("이동·되돌림 중에는 보스 타깃도 놓는다. "
|
|
|
|
|
+ "🔴 이유: 타깃이 있으면 원본 Play_Run() 이 매 폴링 Set_Path(타깃)(MyActor.cs:335)으로 "
|
|
|
|
|
+ "우리 목적지를 덮어써 두 이동이 싸운다. 끄면 813w 그대로(보스 타깃 유지).")]
|
|
|
|
|
public bool pullbackReleasesTarget = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스 재타깃(Change_Target)은 Hold 에서만 한다(락 m_IsLockTarget 은 언제나 유지). "
|
|
|
|
|
+ "끄면 이동 중에도 매 폴링 보스로 되돌린다(= 813w · pullbackReleasesTarget 와 서로 싸운다).")]
|
|
|
|
|
public bool bossRetargetOnlyInHold = true;
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────── 813w2 §1-3 진행 로그
|
|
|
|
|
[Header("진행 로그 (813w2 · D-19 · QA 시각표)")]
|
|
|
|
|
[Tooltip("RunTick 마다 「존 체류 초 · 이동 초 · 목적지 거리 · 최대 이탈」 한 줄을 남기는 주기(초). "
|
|
|
|
|
+ "0 이하면 남기지 않는다. QA 가 존 도착 시각표를 이 줄로 적는다.")]
|
|
|
|
|
public float runTickLogSeconds = 10f;
|
|
|
|
|
|
2026-09-09 01:18:35 +00:00
|
|
|
// ─────────────────────────────────────────── 보스
|
|
|
|
|
[Header("보스 (D-11 · 아레나 이탈 → 리쉬 만피 리셋)")]
|
|
|
|
|
[Tooltip("끄면 게이트 개방·보스 등장 때 아레나로 유도하지 않는다.")]
|
|
|
|
|
public bool bossGuideEnabled = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스가 살아 있는 동안 PC 타깃을 보스로 고정한다(원본 Set_Target(Actor) + m_IsLockTarget). 끄면 원본 타깃 선택 그대로.")]
|
|
|
|
|
public bool bossPriorityEnabled = true;
|
|
|
|
|
|
|
|
|
|
[Tooltip("아레나 중심에 이 거리(m) 안으로 들어오면 도착으로 본다. 813d arenaRadius 20 보다 충분히 작아야 한다.")]
|
|
|
|
|
public float bossArriveRadius = 10f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스전 중 PC 가 아레나 중심에서 이 거리(m) 밖으로 나가면 다시 끌어온다. "
|
|
|
|
|
+ "🔴 813d 리쉬 발동선(arenaRadius + playerLeaveMargin = 24 m)보다 작아야 리셋이 아예 안 일어난다.")]
|
|
|
|
|
public float bossHoldRadius = 14f;
|
|
|
|
|
|
|
|
|
|
[Tooltip("보스에게 원본 락타깃(m_IsLockTarget)을 건다 — 잡몹으로 타깃이 새는 원본 경로 3곳을 함께 막는다. 보스 사망 시 해제.")]
|
|
|
|
|
public bool bossLockTarget = true;
|
|
|
|
|
}
|
|
|
|
|
}
|