352 lines
25 KiB
C#
352 lines
25 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
|
// 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; }
|
|
|
|
/// <summary>
|
|
/// 813w7 프로브용 — 임시 SO 를 잠시 Instance 자리에 끼운다(C8 진리표 · **실에셋은 읽기만** 한다).
|
|
/// null 을 넣으면 캐시가 비워져 다음 접근에서 실에셋을 다시 로드한다(= ClearCache).
|
|
/// 🔴 런타임 게임 경로에서는 절대 부르지 않는다(WLBossArenaSettings.SetInstanceForProbe 선례 그대로).
|
|
/// </summary>
|
|
public static void SetInstanceForProbe(WLRunGuideSettings so)
|
|
{
|
|
s_cached = so;
|
|
s_lookupDone = so != null;
|
|
}
|
|
|
|
// ─────────────────────────────────────────── 전체 스위치
|
|
[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;
|
|
|
|
// ─────────────────────────────────────────── 813w4 §1-1 「다음 런」 재시작 (D-34)
|
|
[Header("「다음 런」 재시작 (813w4 · D-34 · PC 가 시작 좌표에서 166 s 정지)")]
|
|
[Tooltip("🔴 D-34 의 원인: 위 guideOnRunStart 가 off 라 RunStarted 에서 존0 유도를 걸지 않는다. "
|
|
+ "런 1 은 PC 가 맵 진입점(= 존0 교전 범위 안)에서 시작해 원본 자동전투가 대신 해 주지만, "
|
|
+ "RunDirector.Restart() 는 PC 를 부활 좌표(Get_ReincanationPos)로 워프시키므로 존0 에서 33 m 떨어진 "
|
|
+ "빈 자리에 서서 아무 일도 일어나지 않았다. 켜면 reason=\"restart\" 인 RunStarted 에서만 존0 유도를 다시 건다 "
|
|
+ "(런 1 동작은 guideOnRunStart 가 그대로 결정 = C8).")]
|
|
public bool guideOnRestart = true;
|
|
|
|
[Tooltip("재시작 때 가이드 내부 상태를 초기화한다 — 고장(예외) 플래그 해제 + 구독 재보장. "
|
|
+ "끄면 런 1 에서 한 번 예외가 난 가이드가 세션이 끝날 때까지 죽어 있다(D-34 와 같은 화면).")]
|
|
public bool restartResetsGuideState = true;
|
|
|
|
[Tooltip("🔴 정지 감시(안전망): 런이 존 단계인데 가이드 목적지가 없고 PC 가 타깃도 없이 "
|
|
+ "이 시간(초 · unscaled) 동안 제자리면 존0 유도를 건다. 0 이하면 끈다. "
|
|
+ "PC 가 교전 중(타깃 보유)이면 절대 끼어들지 않는다.")]
|
|
public float stallGuideSeconds = 3f;
|
|
|
|
[Tooltip("「제자리」로 보는 이동량(m · XZ). 이 거리 안에서만 움직이면 정지로 센다.")]
|
|
public float stallMoveEpsilon = 0.5f;
|
|
|
|
[Tooltip("존 앵커에 이 거리(m · XZ) 안으로 들어오면 「도착」 = 조종을 놓고 원본 자동전투에 넘긴다.")]
|
|
public float zoneArriveRadius = 6f;
|
|
|
|
[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). "
|
|
+ "이 안의 적은 원본 자동전투가 그대로 잡는다. "
|
|
+ "🔴 813w2: zoneHoldRadius(18) 보다 작게 두되 너무 작으면 존 안의 적을 스스로 버려 화면 내 적이 줄어든다(D-19).")]
|
|
public float guideLeashRadius = 14f;
|
|
|
|
[Tooltip("조종 중 NavMeshAgent.stoppingDistance(m). 목적지 코앞에서 멈춰 서서 도착 판정이 안 나는 것을 막는다.")]
|
|
public float guideStopDistance = 0.5f;
|
|
|
|
[Tooltip("목적지 재지정(SetDestination) 주기(초). 매 폴링마다 부르지 않게 벌린다. 0 이면 매 폴링.")]
|
|
public float repathIntervalSeconds = 0.5f;
|
|
|
|
[Tooltip("PC 가 맞으면 이 시간(초) 동안 가이드를 멈추고 즉시 응전한다(원본 자동전투가 잡는다). 0 이면 응전 중단 없음.")]
|
|
public float resumeAfterHitSeconds = 3f;
|
|
|
|
[Tooltip("한 목적지에 이 시간(초) 안에 도착 못 하면 가이드를 포기한다(경로 불가·지형 끼임 보호). 0 이하면 무제한. "
|
|
+ "포기해도 상태는 Hold 로 떨어져 이탈 감시(되돌림)는 계속한다.")]
|
|
public float maxTravelSeconds = 60f;
|
|
|
|
// ─────────────────────────────────────────── 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;
|
|
|
|
// ─────────────────────────────────────────── 보스
|
|
[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;
|
|
|
|
// ─────────────────────────────────────────── 813w4 §1-2 엘리트 우선 타깃 (D-20)
|
|
[Header("엘리트 우선 타깃 (813w4 · D-20 · 엘리트 TTK 45.2~77.3 s · 목표 8~12 s)")]
|
|
[Tooltip("🔴 D-20 의 원인은 엘리트 HP 가 아니라 「PC 가 엘리트를 안 때린다」였다(Q7 §D-20). "
|
|
+ "켜면 존 Hold 중 eliteTargetRadius 안에 살아 있는 엘리트가 있을 때 타깃을 그 엘리트로 고정한다 "
|
|
+ "(813w 보스 락타깃과 같은 방식). 끄면 원본 타깃 선택 그대로 = 813w3 동작(C8).")]
|
|
public bool elitePriorityEnabled = true;
|
|
|
|
[Tooltip("PC 에서 이 거리(m · XZ) 안의 살아 있는 엘리트를 우선 타깃으로 잡는다. 0 이하면 잡지 않는다.")]
|
|
public float eliteTargetRadius = 12f;
|
|
|
|
[Tooltip("고정한 엘리트가 PC 에서 이 거리(m) 밖으로 벗어나면 고정을 푼다(엘리트 사망·비활성도 해제). "
|
|
+ "잡는 반경보다 커야 반경 경계에서 고정/해제가 떨리지 않는다.")]
|
|
public float eliteReleaseRadius = 18f;
|
|
|
|
[Tooltip("엘리트에게 원본 락타깃(m_IsLockTarget)을 건다 — 잡몹이 타깃을 도로 가져가는 원본 경로를 막는다. 해제 시 원복.")]
|
|
public bool eliteLockTarget = true;
|
|
|
|
[Tooltip("엘리트 재타깃(Change_Target)은 Hold(= 존 반경 안)에서만 한다. 락은 이동 중에도 유지한다. "
|
|
+ "🔴 끄면 이동 중에도 타깃이 걸려 원본 Play_Run() 이 Set_Path(엘리트)로 우리 목적지를 덮어쓴다(813w2 D-22 와 같은 축).")]
|
|
public bool eliteRetargetOnlyInHold = true;
|
|
|
|
// ─────────────────────────────────────────── 813w3 §1-2 강제 복귀(워프)
|
|
[Header("아레나 강제 복귀 (813w3 · D-22 재발 · 이탈 39~56 m)")]
|
|
[Tooltip("되돌림(SetDestination)이 warpBackAfterSeconds 안에 PC 를 반경 안으로 못 넣으면 "
|
|
+ "원본 Set_Warp(Actor.cs:2111 · 813i DeathFlow 의 On_Regen 이 쓰는 그 API)로 아레나로 순간이동시킨다. "
|
|
+ "끄면 813w2 동작 그대로(되돌림만 · C8 롤백 1순위).")]
|
|
public bool warpBackEnabled = true;
|
|
|
|
[Tooltip("이탈(bossHoldRadius 밖)이 이 시간(초 · unscaled) 이상 이어지면 워프한다. 0 이하면 워프하지 않는다.")]
|
|
public float warpBackAfterSeconds = 1.5f;
|
|
|
|
[Tooltip("워프 최소 간격(초 · unscaled). 이 안에 다시 이탈해도 되돌림만 한다(연속 순간이동 방지).")]
|
|
public float warpCooldownSeconds = 5f;
|
|
|
|
[Tooltip("워프 지점 = 아레나 중심 ± 이 반경(m) 안의 임의 지점. 0 이면 정확히 중심.")]
|
|
public float warpJitterRadius = 3f;
|
|
|
|
[Tooltip("워프 지점을 NavMesh 위로 되돌릴 때 쓰는 탐색 반경(m). PC 는 Get_NavSnapRadius()=0 이라 "
|
|
+ "Set_Warp 이 스스로 스냅하지 않는다 → 여기서 NavMesh.SamplePosition 으로 먼저 스냅한다. "
|
|
+ "NavMesh 위 지점을 못 찾으면 워프하지 않는다(지형 뚫기 방지).")]
|
|
public float warpNavSampleRadius = 5f;
|
|
|
|
[Tooltip("워프 직후 카메라 이동 리드(RealCamera.Get_MoveLead().Snap())를 0 으로 스냅해 카메라가 튀지 않게 한다. "
|
|
+ "끄면 리드가 SmoothDamp 로 따라온다.")]
|
|
public bool warpSnapsCameraLead = true;
|
|
|
|
[Tooltip("🔴 보스가 스킬(Anubis 스킬2 Co_JumpToPosition)로 아레나 중심에서 멀어졌을 때는 "
|
|
+ "PC 가 보스에게서 이 거리(m) 안이면 「정상 추적」으로 보고 워프하지 않는다(§1-2).")]
|
|
public float followBossRadius = 12f;
|
|
|
|
[Tooltip("보스가 아레나 중심에서 이 거리(m)를 넘으면 「보스가 멀어졌다」로 본다(위 followBossRadius 판정 조건).")]
|
|
public float bossAwayFromCenterRadius = 6f;
|
|
|
|
// ─────────────────────────────────────────── 813w3 §1-1 이탈 원인 진단
|
|
[Header("이탈 원인 진단 로그 (813w3 · Q7 이 읽는다)")]
|
|
[Tooltip("보스 구간에서 PC 가 bossHoldRadius 를 넘는 순간 「누가 이동을 시켰는지」를 한 줄 남긴다. "
|
|
+ "🔴 verboseLog 와 무관하게 언제나 남긴다(이탈 1회 = 1줄 · 워프 1회 = 1줄). "
|
|
+ "기록은 링버퍼 32칸(사전 할당 · GC 0)에도 함께 쌓인다.")]
|
|
public bool exitDiagEnabled = true;
|
|
|
|
[Tooltip("이탈이 이어지는 동안 이 주기(초)마다 진단 줄을 더 남긴다. 0 이면 이탈 시작·워프 때만(기본).")]
|
|
public float exitDiagRepeatSeconds = 0f;
|
|
|
|
// ─────────────────────────────────────────── 813w7 §1-2 ① 결과창 중 PC 정지(D-45)
|
|
[Header("결과창 중 PC 정지 (813w7 · D-45 결과창 동안 PC 가 존 몹을 소진)")]
|
|
[Tooltip("🔴 런이 끝나면(RunEnded) 다음 런이 시작될 때까지 PC 를 세운다 — 813w 과 같은 "
|
|
+ "Set_JoystickStatus(1)(PCActor.cs:139 이 base.Update() = 자동 교전 접근 루프를 통째로 건너뛴다) + "
|
|
+ "타깃 해제(PCActor.cs:93 자동 스킬 루프는 isTarget 이 있어야만 돈다). "
|
|
+ "Q10 D-45 실측: 결과창 100 초 동안 z0 처치가 26 → 47 로 늘어 다음 런의 사냥터가 비었다. "
|
|
+ "끄면 813w6 그대로(런 종료 = 가이드만 끄고 원본 자동전투 100%).")]
|
|
public bool holdPcOnRunEnd = true;
|
|
|
|
[Tooltip("정지 상한(초 · unscaled). 결과창을 [닫기]로 닫고 런을 다시 시작하지 않는 경우 PC 가 영영 굳지 않게 "
|
|
+ "이 시간이 지나면 스스로 푼다. 0 이면 무제한(다음 RunStarted 까지). "
|
|
+ "🔴 RunGuide 는 UI 를 보지 않는다(UI 0) — 「결과창 닫힘」은 이 상한과 다음 런 시작으로만 판정한다.")]
|
|
public float runEndHoldMaxSeconds = 120f;
|
|
|
|
[Tooltip("정지 중 매 폴링마다 PC 타깃을 뗀다(Actor.Del_Target(true)). "
|
|
+ "🔴 이것이 꺼지면 조이스틱 잠금만으로는 자동 스킬(PCActor.cs:93)이 계속 돌아 몹이 계속 죽는다.")]
|
|
public bool runEndHoldClearsTarget = true;
|
|
|
|
[Tooltip("정지 중 NavMeshAgent 의 남은 경로를 지운다(ResetPath · isStopped). 런 종료 순간의 잔여 경로로 "
|
|
+ "PC 가 계속 걸어가는 것을 막는다. 끄면 좌표만 그대로 두고 조종만 잡는다.")]
|
|
public bool runEndHoldStopsAgent = true;
|
|
|
|
// ─────────────────────────────────────────── 813w8 §1-1 결과창 정지 중 PC 무적(D-46)
|
|
[Tooltip("🔴 결과창 정지 동안 PC 를 무적으로 만든다(813i 무적 API 래치 Survival.SetHoldInvincible). "
|
|
+ "Q11 D-46 실측: 정지는 PC 의 공격만 막고 피격은 그대로여서 결과창을 켜 둔 채 "
|
|
+ "초당 3.4~4.9 HP 가 깎이고 55~80 초에 사망한다(런 3회 3/3). "
|
|
+ "래치는 시간으로 만료되지 않고 정지가 풀릴 때(다음 런 시작 · 상한 만료 · 가이드 off) 함께 풀린다. "
|
|
+ "🔴 사망 중에는 걸지 않는다(부활 우선) — 부활 뒤 첫 정지 틱에서 다시 걸린다. "
|
|
+ "피해 0 은 813i2 피격 창과 같은 원본 무적 경로라 비네트·타격 반응도 0 이다. "
|
|
+ "끄면 813w7 그대로(= D-46 재현).")]
|
|
public bool invincibleWhileRunEndHold = true;
|
|
|
|
// ─────────────────────────────────────────── 813i2 §1-2 보스 예고 중 자동 회피 스텝
|
|
// 🔴 실측 근거 = qa/W2-10_재채점_v8.md §6 — 사망 14/14 가 보스 연타. 813i2 무적 창이 본 방어선이고
|
|
// 이 스텝은 그 앞의 「덜 맞기」다(813r 회피의 축소판 · 실패해도 무적이 받쳐 준다).
|
|
// ■ 예고를 어떻게 읽는가 (원본 수정 0 · 리플렉션 0)
|
|
// 보스의 차징 코루틴(예: Boss_Anubis.Co_Skill1 :64 `AnimationPlay("boss_skill1charging")`)은
|
|
// `Actor.CurAnim` 을 바꾸지 않고(Actor.cs:2029 는 Animator.Play 만 한다) `MobStatus` 는 protected 다.
|
|
// 읽을 수 있는 공개 경로는 **`Actor.m_animation`(public Animator · Actor.cs:30)** 뿐이라
|
|
// `GetCurrentAnimatorStateInfo(0).IsName(<상태 이름>)` 로 판정한다. 상태 이름은 값(C45)이라 아래 배열에 둔다.
|
|
[Header("보스 예고 중 자동 회피 스텝 (813i2 · D-21)")]
|
|
[Tooltip("보스가 예고(차징) 상태에 들어가면 예고 방향 반대로 짧게 한 걸음 물러난다. "
|
|
+ "끄면 813w4 동작 그대로(C8 롤백 1순위).")]
|
|
public bool dodgeStepEnabled = true;
|
|
|
|
[Tooltip("예고로 인정할 보스 애니메이터 상태 이름(0번 레이어 · 대소문자 구분). "
|
|
+ "813b/813h 실측 = 12보스 전부 `boss_skill1charging` / `boss_skill2charging` 두 상태를 쓴다.")]
|
|
public string[] dodgeTelegraphStates = { "boss_skill1charging", "boss_skill2charging" };
|
|
|
|
[Tooltip("한 걸음의 거리(m). 발주서 제안 3 m.")]
|
|
public float dodgeStepDistance = 3f;
|
|
|
|
[Tooltip("한 걸음에 쓰는 시간(초). 거리/시간 = 스텝 속도. 짧을수록 회피가 날카롭고 원본 이동을 오래 막지 않는다.")]
|
|
public float dodgeStepSeconds = 0.3f;
|
|
|
|
[Tooltip("스텝 방향의 옆(직교) 성분 비율. 0 = 보스 반대쪽 정직진(예고 방향의 정반대) · "
|
|
+ "1 = 완전히 옆으로. 돌진형 예고를 옆으로 흘리고 싶으면 올린다.")]
|
|
[Range(0f, 1f)]
|
|
public float dodgeStepSideBias = 0f;
|
|
|
|
[Tooltip("보스가 PC 에서 이 거리(m · XZ) 안일 때만 스텝한다. 멀면 어차피 안 맞는다.")]
|
|
public float dodgeStepBossRadius = 14f;
|
|
|
|
[Tooltip("한 예고(차징 상태가 이어지는 동안)에 스텝은 1회만. 이 시간(초) 안에는 다시 스텝하지 않는다(쿨다운). "
|
|
+ "실측 차징 1.2 s 기준 그보다 커야 예고 1건에 1회가 된다.")]
|
|
public float dodgeStepCooldownSeconds = 1.5f;
|
|
|
|
[Tooltip("스텝 지점을 NavMesh 위로 되돌릴 때 쓰는 탐색 반경(m). NavMesh 밖이면 스텝하지 않는다(지형 뚫기 방지).")]
|
|
public float dodgeStepNavSampleRadius = 2f;
|
|
|
|
[Tooltip("스텝을 시작할 때 Console 에 1줄 남긴다(verboseLog 와 별개 · QA 가 예고→스텝 1회를 이 줄로 센다).")]
|
|
public bool dodgeStepLog = true;
|
|
}
|
|
}
|