diff --git a/apps/client/src/scenes/game/GameManager.ts b/apps/client/src/scenes/game/GameManager.ts index eb1f5a5..87ff08e 100644 --- a/apps/client/src/scenes/game/GameManager.ts +++ b/apps/client/src/scenes/game/GameManager.ts @@ -6,12 +6,12 @@ import { Application, Container, Ticker } from "pixi.js"; import { socketManager } from "@client/network/SocketManager"; import { GameMapController } from "./entities/map/GameMapController"; +import { BombManager } from "./entities/bomb/BombManager"; +import { createBombIdFromPayload } from "./entities/bomb/BombManager"; +import type { BombUpsertPayload } from "./entities/bomb/BombManager"; import { GameTimer } from "./application/GameTimer"; import { GameNetworkSync } from "./application/GameNetworkSync"; import { GameLoop } from "./application/GameLoop"; -import { BombManager } from "./application/BombManager"; -import { createBombIdFromPayload } from "./application/BombManager"; -import type { BombUpsertPayload } from "./application/BombManager"; import type { GamePlayers } from "./application/game.types"; /** ゲームシーンの実行ライフサイクルを管理するマネージャー */ diff --git a/apps/client/src/scenes/game/application/BombManager.ts b/apps/client/src/scenes/game/application/BombManager.ts deleted file mode 100644 index b9caeb8..0000000 --- a/apps/client/src/scenes/game/application/BombManager.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * BombManager - * 爆弾エンティティの生成,更新,破棄を管理する - * クールダウンと設置位置解決をまとめて扱う - */ -import type { Container } from "pixi.js"; -import { config } from "@client/config"; -import type { BombPlacedPayload } from "@repo/shared"; -import { LocalPlayerController } from "@client/scenes/game/entities/player/PlayerController"; -import { BombController } from "@client/scenes/game/entities/bomb/BombController"; -import type { GamePlayers } from "./game.types"; - -/** 経過時間ミリ秒を返す関数型 */ -export type ElapsedMsProvider = () => number; - -/** 爆弾の追加更新に使う入力データ型 */ -export type BombUpsertPayload = BombPlacedPayload & { - radiusGrid?: number; -}; - -/** 爆弾設置時に返す結果型 */ -export type BombPlacementResult = { - bombId: string; - payload: BombPlacedPayload; -}; - -/** 爆弾ペイロードから一意キーを生成する */ -export const createBombIdFromPayload = (payload: BombPlacedPayload): string => { - return `${payload.x}:${payload.y}:${payload.explodeAtElapsedMs}`; -}; - -type BombManagerOptions = { - worldContainer: Container; - players: GamePlayers; - myId: string; - getElapsedMs: ElapsedMsProvider; -}; - -/** 爆弾エンティティのライフサイクルを管理する */ -export class BombManager { - private worldContainer: Container; - private players: GamePlayers; - private myId: string; - private getElapsedMs: ElapsedMsProvider; - private bombs = new Map(); - private lastBombPlacedElapsedMs = Number.NEGATIVE_INFINITY; - - constructor({ worldContainer, players, myId, getElapsedMs }: BombManagerOptions) { - this.worldContainer = worldContainer; - this.players = players; - this.myId = myId; - this.getElapsedMs = getElapsedMs; - } - - /** 自プレイヤー位置に爆弾を設置し,生成IDを返す */ - public placeBomb(): BombPlacementResult | null { - const me = this.players[this.myId]; - if (!me || !(me instanceof LocalPlayerController)) return null; - - const elapsedMs = this.getElapsedMs(); - const { BOMB_COOLDOWN_MS, BOMB_FUSE_MS } = config.GAME_CONFIG; - if (elapsedMs - this.lastBombPlacedElapsedMs < BOMB_COOLDOWN_MS) { - return null; - } - - const position = me.getPosition(); - const payload: BombPlacedPayload = { - x: position.x, - y: position.y, - explodeAtElapsedMs: elapsedMs + BOMB_FUSE_MS, - }; - const bombId = createBombIdFromPayload(payload); - - this.upsertBomb(bombId, payload); - this.lastBombPlacedElapsedMs = elapsedMs; - return { - bombId, - payload, - }; - } - - /** 指定IDの爆弾を追加または更新する */ - public upsertBomb(bombId: string, payload: BombUpsertPayload): void { - const current = this.bombs.get(bombId); - if (current) { - this.worldContainer.removeChild(current.getDisplayObject()); - current.destroy(); - } - - const resolvedPayload = { - ...payload, - radiusGrid: payload.radiusGrid ?? config.GAME_CONFIG.BOMB_RADIUS_GRID, - }; - - const bomb = new BombController(resolvedPayload); - this.bombs.set(bombId, bomb); - this.worldContainer.addChild(bomb.getDisplayObject()); - } - - /** 指定IDの爆弾を削除する */ - public removeBomb(bombId: string): void { - const bomb = this.bombs.get(bombId); - if (!bomb) return; - - this.worldContainer.removeChild(bomb.getDisplayObject()); - bomb.destroy(); - this.bombs.delete(bombId); - } - - /** 爆弾状態を更新し終了済みを破棄する */ - public tick(): void { - const elapsedMs = this.getElapsedMs(); - - this.bombs.forEach((bomb, bombId) => { - bomb.tick(elapsedMs); - - if (bomb.isFinished()) { - this.removeBomb(bombId); - return; - } - }); - } - - /** 管理中の爆弾をすべて破棄する */ - public destroy(): void { - this.bombs.forEach((bomb) => bomb.destroy()); - this.bombs.clear(); - } -} diff --git a/apps/client/src/scenes/game/entities/bomb/BombManager.ts b/apps/client/src/scenes/game/entities/bomb/BombManager.ts new file mode 100644 index 0000000..7691fb2 --- /dev/null +++ b/apps/client/src/scenes/game/entities/bomb/BombManager.ts @@ -0,0 +1,129 @@ +/** + * BombManager + * 爆弾エンティティの生成,更新,破棄を管理する + * クールダウンと設置位置解決をまとめて扱う + */ +import type { Container } from "pixi.js"; +import { config } from "@client/config"; +import type { BombPlacedPayload } from "@repo/shared"; +import { LocalPlayerController } from "@client/scenes/game/entities/player/PlayerController"; +import { BombController } from "./BombController"; +import type { GamePlayers } from "@client/scenes/game/application/game.types"; + +/** 経過時間ミリ秒を返す関数型 */ +export type ElapsedMsProvider = () => number; + +/** 爆弾の追加更新に使う入力データ型 */ +export type BombUpsertPayload = BombPlacedPayload & { + radiusGrid?: number; +}; + +/** 爆弾設置時に返す結果型 */ +export type BombPlacementResult = { + bombId: string; + payload: BombPlacedPayload; +}; + +/** 爆弾ペイロードから一意キーを生成する */ +export const createBombIdFromPayload = (payload: BombPlacedPayload): string => { + return `${payload.x}:${payload.y}:${payload.explodeAtElapsedMs}`; +}; + +type BombManagerOptions = { + worldContainer: Container; + players: GamePlayers; + myId: string; + getElapsedMs: ElapsedMsProvider; +}; + +/** 爆弾エンティティのライフサイクルを管理する */ +export class BombManager { + private worldContainer: Container; + private players: GamePlayers; + private myId: string; + private getElapsedMs: ElapsedMsProvider; + private bombs = new Map(); + private lastBombPlacedElapsedMs = Number.NEGATIVE_INFINITY; + + constructor({ worldContainer, players, myId, getElapsedMs }: BombManagerOptions) { + this.worldContainer = worldContainer; + this.players = players; + this.myId = myId; + this.getElapsedMs = getElapsedMs; + } + + /** 自プレイヤー位置に爆弾を設置し,生成IDを返す */ + public placeBomb(): BombPlacementResult | null { + const me = this.players[this.myId]; + if (!me || !(me instanceof LocalPlayerController)) return null; + + const elapsedMs = this.getElapsedMs(); + const { BOMB_COOLDOWN_MS, BOMB_FUSE_MS } = config.GAME_CONFIG; + if (elapsedMs - this.lastBombPlacedElapsedMs < BOMB_COOLDOWN_MS) { + return null; + } + + const position = me.getPosition(); + const payload: BombPlacedPayload = { + x: position.x, + y: position.y, + explodeAtElapsedMs: elapsedMs + BOMB_FUSE_MS, + }; + const bombId = createBombIdFromPayload(payload); + + this.upsertBomb(bombId, payload); + this.lastBombPlacedElapsedMs = elapsedMs; + return { + bombId, + payload, + }; + } + + /** 指定IDの爆弾を追加または更新する */ + public upsertBomb(bombId: string, payload: BombUpsertPayload): void { + const current = this.bombs.get(bombId); + if (current) { + this.worldContainer.removeChild(current.getDisplayObject()); + current.destroy(); + } + + const resolvedPayload = { + ...payload, + radiusGrid: payload.radiusGrid ?? config.GAME_CONFIG.BOMB_RADIUS_GRID, + }; + + const bomb = new BombController(resolvedPayload); + this.bombs.set(bombId, bomb); + this.worldContainer.addChild(bomb.getDisplayObject()); + } + + /** 指定IDの爆弾を削除する */ + public removeBomb(bombId: string): void { + const bomb = this.bombs.get(bombId); + if (!bomb) return; + + this.worldContainer.removeChild(bomb.getDisplayObject()); + bomb.destroy(); + this.bombs.delete(bombId); + } + + /** 爆弾状態を更新し終了済みを破棄する */ + public tick(): void { + const elapsedMs = this.getElapsedMs(); + + this.bombs.forEach((bomb, bombId) => { + bomb.tick(elapsedMs); + + if (bomb.isFinished()) { + this.removeBomb(bombId); + return; + } + }); + } + + /** 管理中の爆弾をすべて破棄する */ + public destroy(): void { + this.bombs.forEach((bomb) => bomb.destroy()); + this.bombs.clear(); + } +} \ No newline at end of file