Newer
Older
PixelPaintWar / apps / client / src / scenes / game / application / BombHitOrchestrator.ts
/**
 * BombHitOrchestrator
 * 爆弾爆発イベントとローカルプレイヤー情報を橋渡しして当たり判定を実行する
 * 自プレイヤーのみを判定対象とし,Bot被弾はサーバー側で処理する
 */
import { domain } from "@repo/shared";

const { checkBombHit } = domain.game.bombHit;
import type { BombExplodedPayload } from "@client/scenes/game/entities/bomb/BombManager";
import { BombHitContextProvider } from "./BombHitContextProvider";

/** 当たり判定オーケストレーターの初期化入力 */
type BombHitOrchestratorOptions = {
  contextProvider: BombHitContextProvider;
};

/** 爆弾爆発イベントの判定結果を表す型 */
export type BombHitEvaluationResult = {
  status: "duplicate" | "missing-local-player" | "no-hit" | "hit";
  hitPlayerIds: string[];
};

/** 爆弾当たり判定の実行順序を制御する */
export class BombHitOrchestrator {
  private contextProvider: BombHitContextProvider;
  private handledBombIds = new Set<string>();

  constructor({ contextProvider }: BombHitOrchestratorOptions) {
    this.contextProvider = contextProvider;
  }

  /** 爆弾爆発イベントを受けて自プレイヤーの当たり判定を実行し結果を返す */
  public handleBombExploded(payload: BombExplodedPayload): BombHitEvaluationResult {
    if (this.handledBombIds.has(payload.bombId)) {
      return {
        status: "duplicate",
        hitPlayerIds: [],
      };
    }

    this.handledBombIds.add(payload.bombId);
    const reportablePlayers = this.contextProvider.getReportablePlayerCircles();
    if (reportablePlayers.length === 0) {
      return {
        status: "missing-local-player",
        hitPlayerIds: [],
      };
    }

    const hitPlayerIds = reportablePlayers
      .filter((player) => {
        const result = checkBombHit({
          bomb: {
            x: payload.x,
            y: payload.y,
            radius: payload.radius,
            teamId: payload.teamId,
          },
          player,
        });

        return result.isHit;
      })
      .map((player) => player.playerId);

    if (hitPlayerIds.length === 0) {
      return {
        status: "no-hit",
        hitPlayerIds: [],
      };
    }

    return {
      status: "hit",
      hitPlayerIds,
    };

  }

  /** 判定済み状態を初期化する */
  public clear(): void {
    this.handledBombIds.clear();
  }
}