diff --git a/apps/server/src/domains/game/application/services/bot/orchestrators/BotTurnOrchestrator.ts b/apps/server/src/domains/game/application/services/bot/orchestrators/BotTurnOrchestrator.ts index cf2fb6b..470c413 100644 --- a/apps/server/src/domains/game/application/services/bot/orchestrators/BotTurnOrchestrator.ts +++ b/apps/server/src/domains/game/application/services/bot/orchestrators/BotTurnOrchestrator.ts @@ -22,6 +22,7 @@ private readonly hitStunPolicy = new BotHitStunPolicy({ hitStunMs: config.GAME_CONFIG.PLAYER_HIT_STUN_MS, }); + private readonly respawnAtMsByBotId = new Map(); public decide( botPlayerId: BotPlayerId, @@ -42,6 +43,22 @@ stunUntilMs: Number.NEGATIVE_INFINITY, }); + // リスポーン時刻に達していたら初期位置へ座標をリセットする + const respawnAtMs = this.respawnAtMsByBotId.get(botPlayerId); + if (respawnAtMs !== undefined && nowMs >= respawnAtMs) { + this.respawnAtMsByBotId.delete(botPlayerId); + this.stateStore.update(botPlayerId, (state) => ({ + ...state, + targetCol: clamp(Math.floor(player.initialX), 0, GRID_COLS - 1), + targetRow: clamp(Math.floor(player.initialY), 0, GRID_ROWS - 1), + })); + return { + nextX: player.initialX, + nextY: player.initialY, + placeBombPayload: null, + }; + } + if (this.hitStunPolicy.isStunned(nowMs, currentState.stunUntilMs)) { this.stateStore.set(botPlayerId, { ...currentState, @@ -109,7 +126,19 @@ }); } + /** 指定Botへリスポーン用硬直と位置リセットタイマーを適用する */ + public applyRespawnStun(botPlayerId: BotPlayerId, nowMs: number): void { + const respawnStunMs = config.GAME_CONFIG.PLAYER_RESPAWN_STUN_MS; + const respawnAtMs = nowMs + respawnStunMs; + this.respawnAtMsByBotId.set(botPlayerId, respawnAtMs); + this.stateStore.update(botPlayerId, (state) => ({ + ...state, + stunUntilMs: Math.max(state.stunUntilMs, respawnAtMs), + })); + } + public clear(): void { this.stateStore.clear(); + this.respawnAtMsByBotId.clear(); } } diff --git a/apps/server/src/domains/game/entities/player/Player.ts b/apps/server/src/domains/game/entities/player/Player.ts index 79a4a3e..2208683 100644 --- a/apps/server/src/domains/game/entities/player/Player.ts +++ b/apps/server/src/domains/game/entities/player/Player.ts @@ -11,6 +11,11 @@ public y: number = 0; public teamId: number; + /** スポーン時の初期X座標(リスポーン位置として参照する) */ + public initialX: number = 0; + /** スポーン時の初期Y座標(リスポーン位置として参照する) */ + public initialY: number = 0; + /** セルの色を塗り替えた回数 */ public paintCount: number = 0; /** 爆弾を敵に当てた回数 */ diff --git a/apps/server/src/domains/game/entities/player/playerSpawn.ts b/apps/server/src/domains/game/entities/player/playerSpawn.ts index 6315689..62340bd 100644 --- a/apps/server/src/domains/game/entities/player/playerSpawn.ts +++ b/apps/server/src/domains/game/entities/player/playerSpawn.ts @@ -44,5 +44,9 @@ player.x = Math.max(1, Math.min(GRID_COLS - 1, baseX + scatterX)); player.y = Math.max(1, Math.min(GRID_ROWS - 1, baseY + scatterY)); + // リスポーン時に戻る座標として初期位置を保持する + player.initialX = player.x; + player.initialY = player.y; + return player; }; diff --git a/apps/server/src/domains/game/loop/GameLoop.ts b/apps/server/src/domains/game/loop/GameLoop.ts index cb2b7d9..731bdca 100644 --- a/apps/server/src/domains/game/loop/GameLoop.ts +++ b/apps/server/src/domains/game/loop/GameLoop.ts @@ -61,8 +61,8 @@ private lastSentPlayers: Map = new Map(); private disconnectedBotControlledPlayerIds: Set = new Set(); - private botTurnOrchestrator: BotTurnOrchestrator = - new BotTurnOrchestrator(); + private botTurnOrchestrator: BotTurnOrchestrator = new BotTurnOrchestrator(); + private readonly botReceivedHitCountById = new Map(); private readonly roomId: string; private readonly tickRate: number; @@ -218,10 +218,23 @@ }); if (result.isHit) { - this.botTurnOrchestrator.applyHitStun( - player.id as BotPlayerId, - nowMs, - ); + // 被弾カウントを更新し,閾値到達でリスポーンスタン,それ以外は通常スタンを適用する + const prevCount = this.botReceivedHitCountById.get(player.id) ?? 0; + const nextCount = prevCount + 1; + + if (nextCount >= config.GAME_CONFIG.PLAYER_RESPAWN_HIT_COUNT) { + this.botReceivedHitCountById.set(player.id, 0); + this.botTurnOrchestrator.applyRespawnStun( + player.id as BotPlayerId, + nowMs, + ); + } else { + this.botReceivedHitCountById.set(player.id, nextCount); + this.botTurnOrchestrator.applyHitStun( + player.id as BotPlayerId, + nowMs, + ); + } // 爆弾所有者の bombHitCount を加算する const owner = this.players.get(bomb.ownerPlayerId);