Resources/BossRaidWeb/ 리소스를 찾을 수 없습니다.
"; } #endregion #region Private Methods - Events private void SubscribeRaidEvents() { if (_raidManager == null) return; _raidManager.OnRaidStateChanged += OnRaidStateChanged; // Boss 이벤트는 StartRaid 후에 연결됨 — Update에서 동적으로 체크 InvokeRepeating(nameof(CheckBossEvents), 0.5f, 0.5f); } private void UnsubscribeRaidEvents() { if (_raidManager != null) _raidManager.OnRaidStateChanged -= OnRaidStateChanged; CancelInvoke(nameof(CheckBossEvents)); UnsubscribeBossEvents(); } private void CheckBossEvents() { var newBoss = _raidManager?.Boss; if (newBoss != null && newBoss != _bossController) { UnsubscribeBossEvents(); _bossController = newBoss; _safety = _raidManager.Safety; _bossController.OnHPChanged += OnHPChanged; _bossController.OnDamaged += OnDamaged; _bossController.OnPhaseChanged += OnPhaseChanged; _bossController.OnDeath += OnBossDeath; } } private void UnsubscribeBossEvents() { if (_bossController != null) { _bossController.OnHPChanged -= OnHPChanged; _bossController.OnDamaged -= OnDamaged; _bossController.OnPhaseChanged -= OnPhaseChanged; _bossController.OnDeath -= OnBossDeath; _bossController = null; } } private void OnRaidStateChanged(BossRaidManager.RaidState state) => BroadcastState(); private void OnHPChanged(int current, int max) => BroadcastState(); private void OnDamaged(int dmg, bool crit, int hp, int max) => BroadcastState(); private void OnPhaseChanged(int idx, BossData.PhaseData phase) => BroadcastState(); private void OnBossDeath() => BroadcastState(); #endregion #region Private Methods - State private object BuildStateJson() { var boss = _raidManager?.Boss; return new { raidState = _raidManager?.CurrentState.ToString() ?? "Idle", bossName = boss?.Data?.bossName ?? "", currentHP = boss?.CurrentHP ?? 0, maxHP = boss?.MaxHP ?? 0, hpRatio = boss?.HPRatio ?? 0f, phase = boss?.CurrentPhaseIndex ?? 0, phaseName = boss?.CurrentPhase?.phaseName ?? "", bossState = boss?.CurrentState.ToString() ?? "", isDead = boss?.IsDead ?? false, isPaused = _safety?.IsPaused ?? false, hpLockRatio = _safety?.HPLockRatio ?? 0f, bgmVolume = _audio?.BGMVolume ?? 0.5f, sfxVolume = _audio?.SFXVolume ?? 1f, }; } #endregion #region Internal - WebSocket Message Handling internal void HandleWebSocketMessage(string message) { try { var msg = JObject.Parse(message); string action = msg["action"]?.ToString(); _mainThreadActions.Enqueue(() => { switch (action) { case "start_raid": _raidManager?.StartRaid(); break; case "stop_raid": _raidManager?.StopRaid(); break; case "manual_hit": int dmg = msg["damage"]?.Value