Project_WL/Assets/WL/Camera/CombatCameraDirector.cs

474 lines
29 KiB
C#
Raw Permalink Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// CombatCameraDirector.cs — 전투 연출 카메라: 킬캠(줌인+슬로모) · 보스 등장 캠(오빗) · 3타 카메라 리드
//
// PD 지시 #813 · 발주서 WL-811def §1-1 · 설계안 WL-811a §C 811d 행 · 기준서 v1 §B 요소 6·7 · §F-4 (2026-09-09)
//
// ■ 무엇을 하나 (전부 코어 이벤트 **구독** · 원본 훅 0 · RealCamera 무수정)
// Killed(Elite/Boss) → TimeScaleArbiter.Request(Cinematic · cinematicDefaultScale · killCamSlowMoSeconds)
// + RealCamera 궤도 거리(minDistance/maxDistance[/DistanceUp]) × killCamZoomScale 램프 → 유지 → 복원
// 쿨 = 코어 killCamCooldownSeconds · 런당 killCamMaxPerRun · 하단 40% 가드(처치 위치 뷰포트 y)
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// Spawned(isBoss) → arr_cCamData[1] 보스 프리셋 덮어쓰기(옵션 · 원값 스냅샷 후 · 보스가 죽거나 사라지면 원복) · 추적 대상을 보스로
// (공개 필드 target 직접 대입 = list_tfCamactionTarget 무접촉) · Add_Rot 로 sin 오빗 · 거리 배율 · 끝나면 전부 복원
// ComboStage(isLast) → CameraMoveLead.LeadMultiplier = comboLeadMultiplier (comboLeadSeconds 뒤 1 로)
//
// ■ 왜 RealCamera 공개 필드를 매 프레임 쓰는가 (사후 가산 기각 근거 = CameraMoveLead.cs / WLHitFeelCameraShake.cs 주석)
// RealCamera.Update_Cam 은 자기 출력을 다시 입력으로 읽는 저역통과 필터라 transform 에 오프셋을 더하면 13배 반대 방향으로 튄다.
// 대신 그 필터의 **입력**(궤도 거리 파라미터)을 바꾼다: DistanceAway 는 매 LateUpdate 마다 [minDistance, maxDistance] 로 클램프되므로
// 두 값을 같이 줄이면 그 프레임부터 궤도가 줄고 Lerp(smooth 4)가 자연스러운 줌을 만든다. 드로우 +0 · 프리팹 무수정.
//
// ■ 안전장치 (TimeScaleArbiter 규약 승계)
// · 복원은 "우리가 마지막에 쓴 값이 그대로 남아 있을 때만" — Init_Cam/Set_InsideBuilding 등 외부가 덮어썼으면 포기(덮어쓰지 않음)
// · 인트로(CamStatus 0 = IsEnter) 중에는 줌·오빗을 걸지 않는다 · RealCamera 가 없으면(타이틀 등) 슬로모만
// · 보스 캠 중 킬캠 줌은 스킵(슬로모만) · 킬캠 중 보스 스폰은 킬캠을 즉시 복원하고 보스 캠 시작
// · 씬 전환 시 상태·런 카운터 초기화 · 러너 파괴/앱 종료 시 전부 복원
// · 시간은 전부 unscaled(슬로모 중에도 램프가 늘어지지 않는다) · 핸들러 GC 0(verboseLog 때만 문자열)
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
//
// ■ 리뷰 WL-811def FIX 3건 반영 (2026-09-09 · #813)
// FIX-1 킬캠 시작을 **다음 프레임**으로 미루고 그때 base 궤도 값을 재취득 — 처치 스택 뒷부분(MonsterSimpleInfo.Off → Set_LockTarget(null)
// → RealCamera.Init_Cam)이 min/max/DistanceUp 을 덮어써 보스 킬캠 줌이 항상 포기되던 문제(Phase.Pending · KillCamRebased 로 계측)
// FIX-2 arr_cCamData[1](보스/락타겟 프리셋)을 덮어쓸 때 **원값 스냅샷** → 보스 처치·사라짐·씬 전환·SO off 에 복원 · 적용을 busy 스킵 검사 뒤로
// FIX-3 보스 리타겟을 Set_Target(→ list_tfCamactionTarget 누적) 대신 **공개 필드 target 직접 대입**으로 — 리스트 길이 불변
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
using UnityEngine.SceneManagement;
using WL.Combat.Core;
namespace WL.CombatCamera
{
public static class CombatCameraDirector
{
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
enum Phase { None = 0, Pending = 1, ZoomIn = 2, Hold = 3, ZoomOut = 4 }
// ── 킬캠 상태
static Phase s_phase;
static float s_phaseStart, s_killStart;
static float s_scale = 1f;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
static float s_baseMin, s_baseMax, s_baseUp; // 줌 시작 시점 스냅샷(FIX-1 = 처치 다음 프레임에 재취득)
static float s_preMin, s_preMax, s_preUp; // 처치 이벤트 시점 값 — 외부 덮어쓰기 계측용(KillCamRebased)
static float s_wMin, s_wMax, s_wUp; // 마지막으로 우리가 쓴 값(외부 덮어쓰기 감지)
static bool s_scaleUp;
static float s_lastKillCam = -999f;
static int s_runCount;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
static int s_pendingFrame; // FIX-1: 킬캠을 예약한 프레임(이 프레임에는 시작하지 않는다)
static RealCamera s_cam;
// ── 보스 캠 상태
static bool s_bossActive;
static float s_bossStart;
static Transform s_bossTf, s_prevTarget;
static bool s_bossRetargeted;
static float s_bossYawApplied;
static float s_bossBaseMin, s_bossBaseMax, s_bossWMin, s_bossWMax;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// ── 보스 프리셋(arr_cCamData[1]) 상태 — FIX-2: 원값 스냅샷 → 보스 처치·사라짐·씬 전환·SO off 에 복원
static bool s_presetApplied;
static cCamData s_presetData; // 우리가 값을 쓴 프리셋(참조 · 원본 [Serializable] 클래스라 fake-null 아님)
static Actor s_presetBoss; // 프리셋을 건 보스(처치·풀 반납 감지)
static float s_presetOMin, s_presetOMax, s_presetOHeight, s_presetOUp; // 원값
static float s_presetWMin, s_presetWMax, s_presetWHeight, s_presetWUp; // 우리가 쓴 값
// ── 3타 리드 상태
static float s_leadUntil = -1f;
static CombatCameraRunner s_runner;
// ── 진단(프로브가 읽는다)
public static bool Subscribed;
public static int KillCamCount, KillCamSkippedRole, KillCamSkippedCooldown, KillCamSkippedRunCap, KillCamSkippedGuard, KillCamSkippedNoCam, KillCamSkippedBusy, KillCamAbandoned;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
public static int KillCamRebased; // FIX-1: 미룬 사이에 외부(Init_Cam 등)가 궤도 값을 바꿔 base 를 다시 잡은 횟수
public static int BossCamCount, BossCamSkipped, BossPresetApplied, BossPresetRestored, BossPresetAbandoned, ComboLeadCount;
public static ArbiterResult LastSlowMoResult;
public static float LastVictimViewportY = -1f;
public static float LastKillCamMeasured; // 줌 시작 → 완전 복원까지(초 · unscaled)
public static float LastBossCamMeasured;
public static string LastInfo = "";
public static bool KillCamActive { get { return s_phase != Phase.None; } }
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
public static bool KillCamPending { get { return s_phase == Phase.Pending; } }
public static bool BossCamActive { get { return s_bossActive; } }
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
public static bool BossPresetActive { get { return s_presetApplied; } }
public static float CurrentZoomScale { get { return s_scale; } }
public static int RunCount { get { return s_runCount; } }
public static float LeadUntil { get { return s_leadUntil; } }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void Register()
{
CombatEvents.Killed.Add(OnKilled);
CombatEvents.Spawned.Add(OnSpawned);
CombatEvents.ComboStage.Add(OnComboStage);
SceneManager.activeSceneChanged += OnSceneChanged;
Subscribed = true;
}
static WLCombatCameraSettings St { get { return WLCombatCameraSettings.Instance; } }
static bool Verbose { get { var st = St; return st != null && st.verboseLog; } }
static void Log(string msg)
{
var st = St;
if (st != null && st.verboseLog) Debug.Log("[CombatCameraDirector] " + msg);
}
static CombatCameraRunner Runner
{
get
{
if (s_runner == null)
{
var go = new GameObject("__WLCombatCamera");
Object.DontDestroyOnLoad(go);
s_runner = go.AddComponent<CombatCameraRunner>();
}
return s_runner;
}
}
static RealCamera ResolveCamera() { return MyValue.m_RealCamera; }
// ───────────────────────────────────────── Killed → 킬캠
static void OnKilled(in KilledEvent e)
{
if (!WLCombatCameraSettings.Enabled) return;
var st = St;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// FIX-2: 프리셋을 건 보스가 죽었으면 즉시 원복(틱 폴백보다 한 프레임 빠르다).
// Set_Die 는 이 뒤에 Set_LockTarget(null) → Init_Cam() 으로 인덱스 0 을 읽으므로 여기서 되돌려도 이번 프레임 연출에 영향 0.
if (s_presetApplied && ReferenceEquals(e.victim, s_presetBoss)) RestoreBossPreset();
if (!st.killCamEnabled) return;
bool roleOk = e.subRole == eSubRol.Boss ? st.killCamOnBoss : e.subRole == eSubRol.Elite ? st.killCamOnElite : st.killCamOnNormal;
if (!roleOk || (st.killCamDirectHitOnly && !e.byDirectHit)) { KillCamSkippedRole++; return; }
float now = Time.unscaledTime;
var core = WLCombatCoreSettings.Instance;
float cooldown = core != null ? core.killCamCooldownSeconds : 0f;
if (now - s_lastKillCam < cooldown) { KillCamSkippedCooldown++; LastInfo = "cooldown"; if (Verbose) Log("킬캠 스킵(쿨) " + (now - s_lastKillCam).ToString("F2") + " < " + cooldown); return; }
if (st.killCamMaxPerRun > 0 && s_runCount >= st.killCamMaxPerRun) { KillCamSkippedRunCap++; LastInfo = "runcap"; if (Verbose) Log("킬캠 스킵(런 상한 " + st.killCamMaxPerRun + ")"); return; }
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// 쿨·런 카운터는 여기서 소모한다 — 아래 스킵(카메라 없음·인트로·하단 가드)으로 슬로모만 나가도 "킬캠 1회를 썼다"로 친다.
// 의도: 연출 밀도(런당 ≤6 · 최소 간격 8 s)는 슬로모까지 포함한 체감 기준이다(리뷰 Minor-6 확인 사항).
s_lastKillCam = now;
s_runCount++;
KillCamCount++;
// ① 슬로모 — 중재자 Cinematic 층(히트스톱은 이 동안 SkippedHigher · D-1)
float slow = core != null ? core.cinematicDefaultScale : 1f;
LastSlowMoResult = TimeScaleArbiter.Request(TimeLayer.Cinematic, slow, st.killCamSlowMoSeconds);
// ② 줌 — RealCamera 가 있고 인트로가 아니고 보스 캠 중이 아닐 때
var cam = ResolveCamera();
if (cam == null) { KillCamSkippedNoCam++; LastInfo = "slowmo-only(no cam)"; if (Verbose) Log("킬캠 줌 스킵(RealCamera 없음) 슬로모=" + LastSlowMoResult); return; }
if (cam.IsEnter() || s_bossActive) { KillCamSkippedBusy++; LastInfo = "slowmo-only(busy)"; if (Verbose) Log("킬캠 줌 스킵(인트로/보스 캠) 슬로모=" + LastSlowMoResult); return; }
if (st.killCamBottomGuardViewportY > 0f)
{
var uc = Camera.main;
if (uc != null)
{
var v = uc.WorldToViewportPoint(e.position);
LastVictimViewportY = v.y;
if (v.z > 0f && v.y < st.killCamBottomGuardViewportY)
{
KillCamSkippedGuard++; LastInfo = "slowmo-only(bottom guard)";
if (Verbose) Log("킬캠 줌 스킵(하단 가드 y=" + v.y.ToString("F2") + " < " + st.killCamBottomGuardViewportY + ") 슬로모=" + LastSlowMoResult);
return;
}
}
}
if (s_phase != Phase.None) EndZoom(true); // (쿨 안이라 사실상 없음) 안전
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// ── FIX-1(리뷰 Major-1): 여기서 바로 스냅샷·줌을 걸면 같은 호출 스택 뒷부분이 그 값을 덮어쓴다.
// MobActor.Set_Die 는 RaiseKilled(:435) 다음에 MonsterSimpleInfo.DeadTarget(:444) → Off() → 보스면 Set_LockTarget(null)
// → RealCamera.Init_Cam() 으로 minDistance/maxDistance/cameraHeight 와 Set_DistanceUp(false) 로 DistanceUp 을 프리셋 값으로 다시 쓴다.
// → 다음 틱의 ZoomStillOurs() 가 false → KillCamAbandoned · 보스 킬캠은 슬로모만 남았다.
// 그래서 **시작을 다음 프레임으로 미루고 그때 base 를 재취득**한다(엘리트/일반은 Set_LockTarget 경로가 없어 값이 같다 = 회귀 0 · 지연 1프레임).
s_cam = cam;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
s_preMin = cam.minDistance; s_preMax = cam.maxDistance; s_preUp = cam.DistanceUp;
s_scaleUp = st.killCamScaleDistanceUp;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
s_scale = 1f;
s_phase = Phase.Pending; s_pendingFrame = Time.frameCount; s_phaseStart = s_killStart = now;
Runner.enabled = true;
LastInfo = "killcam";
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
if (Verbose) Log("킬캠 예약(다음 프레임 base 재취득) victim=" + (e.victim != null ? e.victim.name : "?") + " sub=" + e.subRole + " 슬로모=" + LastSlowMoResult + " pre=" + s_preMin.ToString("F2") + "/" + s_preMax.ToString("F2") + "/" + s_preUp.ToString("F2"));
}
/// <summary>FIX-1: 예약해 둔 킬캠을 다음 프레임에 시작한다 — 처치 프레임의 나머지 로직(Init_Cam 등)이 다 쓴 뒤의 값을 base 로 잡는다.</summary>
static void BeginZoom(float now)
{
if (Time.frameCount <= s_pendingFrame) return; // 처치와 같은 프레임에는 시작하지 않는다
var cam = s_cam;
if (cam.IsEnter() || s_bossActive) // 미룬 사이에 인트로/보스 캠이 시작됐으면 슬로모만
{
KillCamSkippedBusy++; LastInfo = "slowmo-only(busy)";
s_phase = Phase.None; s_cam = null; LastKillCamMeasured = -1f;
if (Verbose) Log("킬캠 줌 취소(예약 중 인트로/보스 캠)");
return;
}
s_baseMin = cam.minDistance; s_baseMax = cam.maxDistance; s_baseUp = cam.DistanceUp;
if (!Mathf.Approximately(s_baseMin, s_preMin) || !Mathf.Approximately(s_baseMax, s_preMax) || !Mathf.Approximately(s_baseUp, s_preUp)) KillCamRebased++;
s_phase = Phase.ZoomIn; s_phaseStart = now;
WriteZoom(1f);
if (Verbose) Log("킬캠 시작 base=" + s_baseMin.ToString("F2") + "/" + s_baseMax.ToString("F2") + "/" + s_baseUp.ToString("F2") + " rebased=" + KillCamRebased);
}
static void WriteZoom(float k)
{
var cam = s_cam;
s_scale = k;
cam.minDistance = s_wMin = s_baseMin * k;
cam.maxDistance = s_wMax = s_baseMax * k;
if (s_scaleUp) cam.DistanceUp = s_wUp = s_baseUp * k;
}
static bool ZoomStillOurs()
{
var cam = s_cam;
if (cam == null) return false;
if (!Mathf.Approximately(cam.minDistance, s_wMin) || !Mathf.Approximately(cam.maxDistance, s_wMax)) return false;
if (s_scaleUp && !Mathf.Approximately(cam.DistanceUp, s_wUp)) return false;
return true;
}
static void EndZoom(bool restore)
{
if (s_phase == Phase.None) return;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
if (s_phase == Phase.Pending) // 아직 아무것도 쓰지 않았다 — 복원할 것도, 포기로 셀 것도 없다
{
s_phase = Phase.None; s_scale = 1f; s_cam = null;
if (Verbose) Log("킬캠 예약 취소");
return;
}
if (restore && ZoomStillOurs())
{
var cam = s_cam;
cam.minDistance = s_baseMin; cam.maxDistance = s_baseMax;
if (s_scaleUp) cam.DistanceUp = s_baseUp;
LastKillCamMeasured = Time.unscaledTime - s_killStart;
}
else if (restore) { KillCamAbandoned++; LastKillCamMeasured = -1f; }
s_phase = Phase.None; s_scale = 1f; s_cam = null;
if (Verbose) Log("킬캠 종료 restore=" + restore + " measured=" + LastKillCamMeasured.ToString("F3"));
}
static void TickKillCam(float now)
{
if (s_phase == Phase.None) return;
var st = St;
if (s_cam == null || st == null || !WLCombatCameraSettings.Enabled) { EndZoom(true); return; }
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
if (s_phase == Phase.Pending) { BeginZoom(now); return; } // FIX-1: 예약 → 다음 프레임 시작(base 재취득)
if (!ZoomStillOurs()) { KillCamAbandoned++; s_phase = Phase.None; s_scale = 1f; s_cam = null; LastKillCamMeasured = -1f; if (Verbose) Log("킬캠 포기(외부가 거리 필드를 바꿈)"); return; }
float t = now - s_phaseStart;
float zoom = st.killCamZoomScale;
switch (s_phase)
{
case Phase.ZoomIn:
if (t >= st.killCamZoomInSeconds) { WriteZoom(zoom); s_phase = Phase.Hold; s_phaseStart = now; }
else WriteZoom(Mathf.Lerp(1f, zoom, Mathf.SmoothStep(0f, 1f, t / Mathf.Max(st.killCamZoomInSeconds, 0.0001f))));
break;
case Phase.Hold:
if (t >= st.killCamHoldSeconds) { s_phase = Phase.ZoomOut; s_phaseStart = now; }
break;
case Phase.ZoomOut:
if (t >= st.killCamZoomOutSeconds) EndZoom(true);
else WriteZoom(Mathf.Lerp(zoom, 1f, Mathf.SmoothStep(0f, 1f, t / Mathf.Max(st.killCamZoomOutSeconds, 0.0001f))));
break;
}
}
// ───────────────────────────────────────── Spawned(isBoss) → 보스 캠
static void OnSpawned(in SpawnedEvent e)
{
if (!WLCombatCameraSettings.Enabled) return;
var st = St;
if (!st.bossCamEnabled || !e.isBoss || e.actor == null) return;
var cam = ResolveCamera();
if (cam == null) { BossCamSkipped++; LastInfo = "bosscam(no cam)"; return; }
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// FIX-2: 프리셋 적용은 busy 스킵 검사 **뒤**로 — 보스 캠이 스킵되는데 프리셋만 써지는 일이 없게 한다.
if (cam.IsEnter() || s_bossActive) { BossCamSkipped++; LastInfo = "bosscam(busy)"; return; }
if (s_phase != Phase.None) EndZoom(true);
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
ApplyBossPreset(cam, st, e.actor);
float now = Time.unscaledTime;
s_cam = cam;
s_bossActive = true; s_bossStart = now;
s_bossTf = e.actor.transform;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// ── FIX-3(리뷰 Major-3): RealCamera.Set_Target 은 호출마다 list_tfCamactionTarget 에 항목을 쌓고(원본 :77-86),
// 그 리스트는 인트로(CamStatus 0) 분기에서만 소비된다(:197·212) → 보스 캠 1회당 2건이 영구히 남아 다음 인트로/컷 경로가
// 우리 잔재를 첫 목표로 삼고 Start_Game() 이 지연된다. 그래서 **공개 필드 target 을 직접 대입**해 리스트를 건드리지 않는다.
// · isTarget(private)은 target 이 이미 있을 때만 true 이므로 기존 대상이 없으면 아예 리타겟하지 않는다(카메라 정지 방지).
// · m_Target(private Actor)은 이전 대상(PC)인 채로 남는다 = CameraMoveLead 의 "기준 속도" 출처만 PC 값
// (리드 자체는 target 의 이동 차분으로 계산 · speedReference > 0 이면 애초에 안 쓴다) · 오빗이 끝나면 다시 정합.
// · 완전 정합이 필요하면 811n 에 Add_CamActionTarget 없는 Set_Target 오버로드를 요청한다(완료보고 기재).
s_prevTarget = cam.target;
s_bossRetargeted = false;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
if (st.bossCamRetarget && s_prevTarget != null && s_bossTf != null) { cam.target = s_bossTf; s_bossRetargeted = true; }
s_bossBaseMin = cam.minDistance; s_bossBaseMax = cam.maxDistance;
cam.minDistance = s_bossWMin = s_bossBaseMin * st.bossCamDistanceScale;
cam.maxDistance = s_bossWMax = s_bossBaseMax * st.bossCamDistanceScale;
s_bossYawApplied = 0f;
BossCamCount++;
Runner.enabled = true;
LastInfo = "bosscam";
if (Verbose) Log("보스 캠 시작 boss=" + e.actor.name + " prevTarget=" + (s_prevTarget != null ? s_prevTarget.name : "null") + " retarget=" + s_bossRetargeted);
}
static void EndBossCam(bool restore)
{
if (!s_bossActive) return;
var cam = s_cam;
if (restore && cam != null)
{
if (Mathf.Abs(s_bossYawApplied) > 1e-4f) cam.Add_Rot(-s_bossYawApplied);
if (Mathf.Approximately(cam.minDistance, s_bossWMin) && Mathf.Approximately(cam.maxDistance, s_bossWMax))
{
cam.minDistance = s_bossBaseMin; cam.maxDistance = s_bossBaseMax;
}
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// FIX-3: 복원도 공개 필드 대입(리스트 무접촉) · 외부가 대상을 바꿨으면 손대지 않음
if (s_bossRetargeted && s_prevTarget != null && ReferenceEquals(cam.target, s_bossTf)) cam.target = s_prevTarget;
LastBossCamMeasured = Time.unscaledTime - s_bossStart;
}
s_bossActive = false; s_bossTf = null; s_prevTarget = null; s_bossYawApplied = 0f; s_cam = null;
if (Verbose) Log("보스 캠 종료 restore=" + restore + " measured=" + LastBossCamMeasured.ToString("F3"));
}
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
// ───────────────────────────────────────── 보스 프리셋(arr_cCamData[1]) — FIX-2
/// <summary>보스/락타겟 프리셋을 덮어쓰기 전에 원값을 스냅샷한다. 이미 걸려 있으면 스냅샷을 덮어쓰지 않는다(중첩 보스).</summary>
static void ApplyBossPreset(RealCamera cam, WLCombatCameraSettings st, Actor boss)
{
if (!st.bossPresetApply || s_presetApplied) return;
var arr = cam.arr_cCamData;
if (arr == null || arr.Length <= 1 || arr[1] == null) return;
var d = arr[1];
s_presetData = d; s_presetBoss = boss;
s_presetOMin = d.minDistance; s_presetOMax = d.maxDistance; s_presetOHeight = d.cameraHeight; s_presetOUp = d.DistanceUp;
d.minDistance = s_presetWMin = st.bossPresetDistance;
d.maxDistance = s_presetWMax = st.bossPresetDistance;
d.cameraHeight = s_presetWHeight = st.bossPresetCameraHeight;
if (st.bossPresetDistanceUp >= 0f) d.DistanceUp = st.bossPresetDistanceUp;
s_presetWUp = d.DistanceUp;
s_presetApplied = true;
BossPresetApplied++;
if (Verbose) Log("보스 프리셋 적용 [1] " + s_presetOMin.ToString("F2") + "/" + s_presetOMax.ToString("F2") + "/" + s_presetOHeight.ToString("F0") + "° → " + s_presetWMin.ToString("F2") + "/" + s_presetWMax.ToString("F2") + "/" + s_presetWHeight.ToString("F0") + "°");
}
/// <summary>프리셋을 원값으로 되돌린다(보스 처치·사라짐·씬 전환·SO off·러너 파괴). 파일 규약대로 "우리가 쓴 값이 그대로 남아 있을 때만" 되돌린다.</summary>
static void RestoreBossPreset()
{
if (!s_presetApplied) return;
var d = s_presetData;
s_presetApplied = false; s_presetData = null; s_presetBoss = null;
if (d == null) return;
if (!Mathf.Approximately(d.minDistance, s_presetWMin) || !Mathf.Approximately(d.maxDistance, s_presetWMax)
|| !Mathf.Approximately(d.cameraHeight, s_presetWHeight) || !Mathf.Approximately(d.DistanceUp, s_presetWUp))
{
BossPresetAbandoned++;
if (Verbose) Log("보스 프리셋 복원 포기(외부가 값을 바꿈)");
return;
}
d.minDistance = s_presetOMin; d.maxDistance = s_presetOMax; d.cameraHeight = s_presetOHeight; d.DistanceUp = s_presetOUp;
BossPresetRestored++;
if (Verbose) Log("보스 프리셋 복원 [1] " + s_presetOMin.ToString("F2") + "/" + s_presetOMax.ToString("F2") + "/" + s_presetOHeight.ToString("F0") + "°");
}
/// <summary>프리셋 수명 감시 — 보스가 죽거나(IsDead) 풀로 돌아가거나(비활성) 파괴되면, SO 가 꺼지면 원값으로 돌린다.</summary>
static void TickBossPreset()
{
if (!s_presetApplied) return;
if (!WLCombatCameraSettings.Enabled) { RestoreBossPreset(); return; } // C8 — 끄면 즉시 원본 값
var b = s_presetBoss;
if (b == null || b.IsDead()) RestoreBossPreset(); // IsDead() = DeadStatus || !activeInHierarchy(풀 반납)
}
static void TickBossCam(float now)
{
if (!s_bossActive) return;
var st = St;
if (s_cam == null || st == null || !WLCombatCameraSettings.Enabled) { EndBossCam(true); return; }
float dur = Mathf.Max(st.bossCamSeconds, 0.0001f);
float t = (now - s_bossStart) / dur;
if (t >= 1f) { EndBossCam(true); return; }
float yaw = Mathf.Sin(t * Mathf.PI) * st.bossCamOrbitDegrees;
s_cam.Add_Rot(yaw - s_bossYawApplied);
s_bossYawApplied = yaw;
}
// ───────────────────────────────────────── ComboStage(isLast) → 3타 리드
static void OnComboStage(in ComboStageEvent e)
{
if (!WLCombatCameraSettings.Enabled) return;
var st = St;
if (!st.comboLeadEnabled || !e.isLast) return;
CameraMoveLead.LeadMultiplier = st.comboLeadMultiplier;
s_leadUntil = Time.unscaledTime + st.comboLeadSeconds;
ComboLeadCount++;
Runner.enabled = true;
if (Verbose) Log("3타 리드 ×" + st.comboLeadMultiplier + " " + st.comboLeadSeconds + " s clip=" + e.clip);
}
static void TickLead(float now)
{
if (s_leadUntil >= 0f && now >= s_leadUntil) { CameraMoveLead.LeadMultiplier = 1f; s_leadUntil = -1f; }
}
// ───────────────────────────────────────── 공용
internal static void Tick()
{
float now = Time.unscaledTime;
TickKillCam(now);
TickBossCam(now);
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
TickBossPreset();
TickLead(now);
}
/// <summary>활성 연출을 전부 끝내고 카메라 값을 복원한다(러너 파괴 · 앱 종료 · 프로브 정리).</summary>
public static void ReleaseAll()
{
EndZoom(true);
EndBossCam(true);
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
RestoreBossPreset();
CameraMoveLead.LeadMultiplier = 1f; s_leadUntil = -1f;
}
static void OnSceneChanged(Scene from, Scene to)
{
// 카메라 오브젝트가 사라졌을 수 있다 — 복원 없이 상태만 버린다(복원은 ReleaseAll 이 러너 OnDisable 에서 시도)
s_phase = Phase.None; s_scale = 1f; s_cam = null;
s_bossActive = false; s_bossTf = null; s_prevTarget = null; s_bossYawApplied = 0f;
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
RestoreBossPreset(); // FIX-2: 프리셋은 순수 데이터(cCamData)라 카메라가 파괴돼도 되돌리는 쪽이 안전하다(살아 있으면 원값 복귀)
CameraMoveLead.LeadMultiplier = 1f; s_leadUntil = -1f;
s_runCount = 0; s_lastKillCam = -999f;
}
/// <summary>프로브용: 런 카운터·쿨 초기화(연출 상태는 유지).</summary>
public static void ResetRun() { s_runCount = 0; s_lastKillCam = -999f; }
/// <summary>프로브·QA 용: 쿨만 초기화(런 카운터 유지 → 런 상한 검증).</summary>
public static void ResetCooldown() { s_lastKillCam = -999f; }
/// <summary>프로브용 카운터 초기화.</summary>
public static void ResetDiagnostics()
{
[WL-811def-fix] 카메라 리뷰 FIX 3 + Minor (#813) 리뷰 reviews/WL-811def.md 의 Major 3 · Minor 5 반영. Assets/Script/** 0줄 · Core 0 · RealCamera.cs 0(공개 필드/메서드만). FIX-1 보스 킬캠 포기: 처치 스택 뒷부분(MobActor.Set_Die → MonsterSimpleInfo.DeadTarget → Off → Set_LockTarget(null) → RealCamera.Init_Cam)이 궤도 3필드를 덮어써 ZoomStillOurs() 가 항상 false 였다. Phase.Pending 을 추가해 킬캠 시작을 다음 프레임으로 미루고 그때 base 를 재취득한다(KillCamRebased 로 계측). 엘리트/일반은 Set_LockTarget 경로가 없어 회귀 0. FIX-2 arr_cCamData[1] 영구 덮어쓰기: 원값 스냅샷 → 보스 처치(OnKilled)·사라짐/비활성(TickBossPreset)·씬 전환·SO off· ReleaseAll 에 복원(BossPresetRestored/Abandoned). 프리셋 적용을 busy 스킵 검사 뒤로 옮겨 스킵된 스폰은 프리셋도 안 쓴다. FIX-3 Set_Target 누적: 보스 리타겟을 공개 필드 target 직접 대입으로 바꿔 list_tfCamactionTarget 길이 불변(기존 대상이 있을 때만 리타겟 = isTarget 유효). 완전 정합용 Set_Target 오버로드는 811n 요청 대상. Minor: MobHitFlash.OnKilled 에 WLReactionRunner.Ensure() · DamageAggregator 재진입 가드(Harvest/Dispatch 분리 + s_dispatching 백업·복원 · ReentrantHits) · SO 폴백 상수 제거(KillChain/DamageAggregator Tick) · BestChainEver 중복 갱신 1곳으로 · s_burstHeld↔EffectBudget 불변식 주석 + 홀드 만료 시 선반납 · 킬캠 쿨/런 카운터 소모 의도 주석. 프로브: WL811def_Probe.FixCheck 추가(FIX-1 Init_Cam 덮어쓰기 뒤 줌 유지 · FIX-2 프리셋 원복/busy 순서 · FIX-3 리스트 길이 불변). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:47 +00:00
KillCamCount = KillCamSkippedRole = KillCamSkippedCooldown = KillCamSkippedRunCap = KillCamSkippedGuard = KillCamSkippedNoCam = KillCamSkippedBusy = KillCamAbandoned = KillCamRebased = 0;
BossCamCount = BossCamSkipped = BossPresetApplied = BossPresetRestored = BossPresetAbandoned = ComboLeadCount = 0;
LastKillCamMeasured = LastBossCamMeasured = 0f; LastVictimViewportY = -1f; LastInfo = "";
}
}
/// <summary>연출 카메라 틱 러너(숨은 오브젝트 1개 · DontDestroyOnLoad). 파괴·비활성·종료 시 카메라 값을 반드시 되돌린다.</summary>
public sealed class CombatCameraRunner : MonoBehaviour
{
void Update() { CombatCameraDirector.Tick(); }
void OnDisable() { CombatCameraDirector.ReleaseAll(); }
void OnApplicationQuit() { CombatCameraDirector.ReleaseAll(); }
}
}