/**
* CombatLifecycleFacade
* 被弾判定から硬直,演出,報告送信までの戦闘ライフサイクルを管理する
* ゲームマネージャーから被弾関連の責務を分離する
*/
import { config } from "@client/config";
import type { PlayerDeadPayload } from "@repo/shared";
import type { BombExplodedPayload } from "@client/scenes/game/entities/bomb/BombManager";
import { BombHitContextProvider } from "@client/scenes/game/application/BombHitContextProvider";
import { BombHitOrchestrator } from "@client/scenes/game/application/BombHitOrchestrator";
import { PlayerDeathPolicy } from "@client/scenes/game/application/PlayerDeathPolicy";
import { PlayerHitEffectOrchestrator } from "@client/scenes/game/application/PlayerHitEffectOrchestrator";
import type { GamePlayers } from "@client/scenes/game/application/game.types";
import { HitReportPolicy } from "./HitReportPolicy";
/** CombatLifecycleFacade の初期化入力 */
export type CombatLifecycleFacadeOptions = {
players: GamePlayers;
myId: string;
acquireInputLock: () => () => void;
onSendBombHitReport: (bombId: string, targetPlayerId: string) => void;
};
/** 被弾関連ライフサイクルの制御を担当する */
export class CombatLifecycleFacade {
private readonly myId: string;
private readonly onSendBombHitReport: (
bombId: string,
targetPlayerId: string,
) => void;
private readonly bombHitOrchestrator: BombHitOrchestrator;
private readonly playerDeathPolicy: PlayerDeathPolicy;
private readonly playerHitEffectOrchestrator: PlayerHitEffectOrchestrator;
private readonly hitReportPolicy = new HitReportPolicy();
constructor({
players,
myId,
acquireInputLock,
onSendBombHitReport,
}: CombatLifecycleFacadeOptions) {
this.myId = myId;
this.onSendBombHitReport = onSendBombHitReport;
this.bombHitOrchestrator = new BombHitOrchestrator({
contextProvider: new BombHitContextProvider({
players,
myId,
}),
});
this.playerDeathPolicy = new PlayerDeathPolicy({
myId,
hitStunMs: config.GAME_CONFIG.PLAYER_HIT_STUN_MS,
acquireInputLock,
});
this.playerHitEffectOrchestrator = new PlayerHitEffectOrchestrator({
players,
blinkDurationMs: config.GAME_CONFIG.PLAYER_HIT_EFFECT.BLINK_DURATION_MS,
dedupWindowMs: config.GAME_CONFIG.PLAYER_HIT_EFFECT.DEDUP_WINDOW_MS,
});
}
/** 爆弾爆発時の判定と後続処理を実行する */
public handleBombExploded(payload: BombExplodedPayload): void {
const result = this.bombHitOrchestrator.handleBombExploded(payload);
if (!result.hitPlayerId) return;
this.playerDeathPolicy.applyLocalHitStun();
this.playerHitEffectOrchestrator.handleLocalBombHit(this.myId);
if (this.hitReportPolicy.shouldSendReport(result.status, payload.bombId, result.hitPlayerId)) {
this.onSendBombHitReport(payload.bombId, result.hitPlayerId);
}
}
/** ネットワーク被弾通知を適用する */
public handleNetworkPlayerDead(payload: PlayerDeadPayload): void {
this.playerDeathPolicy.applyPlayerDeadEvent(payload);
this.playerHitEffectOrchestrator.handleNetworkPlayerDead(payload.playerId, this.myId);
}
/** 管理中リソースを破棄する */
public dispose(): void {
this.bombHitOrchestrator.clear();
this.playerDeathPolicy.dispose();
this.hitReportPolicy.clear();
}
}