import sinon from "sinon"; import { GameLoop } from "../../../src/core/GameLoop.js"; /** * Creates a basic GameLoop setup for tests. * @returns {{ gameLoop: GameLoop; container: HTMLElement }} */ export function createGameLoopSetup() { const container = document.createElement("div"); document.body.appendChild(container); const gameLoop = new GameLoop(); return { gameLoop, container }; } /** * Cleans up GameLoop after tests. * @param {GameLoop} gameLoop * @param {HTMLElement} container */ export function cleanupGameLoop(gameLoop, container) { gameLoop.stop(); if (container.parentNode) { container.parentNode.removeChild(container); } // Cleanup Three.js resources if possible to avoid context loss limits if (gameLoop.renderer) { gameLoop.renderer.dispose(); gameLoop.renderer.forceContextLoss(); } } /** * Creates a mock game state manager for deployment phase. * @returns {Object} */ export function createMockGameStateManagerForDeployment() { return { currentState: "STATE_DEPLOYMENT", transitionTo: sinon.stub(), setCombatState: sinon.stub(), getCombatState: sinon.stub().returns(null), }; } /** * Creates a mock game state manager for combat phase. * @returns {Object} */ export function createMockGameStateManagerForCombat() { return { currentState: "STATE_COMBAT", transitionTo: sinon.stub(), setCombatState: sinon.stub(), getCombatState: sinon.stub(), }; } /** * Creates a mock mission manager with enemy spawns. * @param {Array} enemySpawns * @returns {Object} */ export function createMockMissionManager(enemySpawns = []) { const mockMissionDef = { id: "MISSION_TEST", config: { title: "Test Mission" }, enemy_spawns: enemySpawns, objectives: { primary: [] }, }; return { getActiveMission: sinon.stub().returns(mockMissionDef), }; } /** * Creates basic run data for tests. * @param {Object} overrides * @returns {Object} */ export function createRunData(overrides = {}) { return { seed: 12345, depth: 1, squad: [], ...overrides, }; } /** * Sets up combat test units. * @param {GameLoop} gameLoop * @returns {{ playerUnit: Object; enemyUnit: Object }} */ export function setupCombatUnits(gameLoop) { const playerUnit = gameLoop.unitManager.createUnit("CLASS_VANGUARD", "PLAYER"); playerUnit.baseStats.movement = 4; playerUnit.baseStats.speed = 10; playerUnit.currentAP = 10; playerUnit.chargeMeter = 100; playerUnit.position = { x: 5, y: 1, z: 5 }; gameLoop.grid.placeUnit(playerUnit, playerUnit.position); gameLoop.createUnitMesh(playerUnit, playerUnit.position); const enemyUnit = gameLoop.unitManager.createUnit("ENEMY_DEFAULT", "ENEMY"); enemyUnit.baseStats.speed = 8; enemyUnit.chargeMeter = 80; enemyUnit.position = { x: 15, y: 1, z: 15 }; gameLoop.grid.placeUnit(enemyUnit, enemyUnit.position); gameLoop.createUnitMesh(enemyUnit, enemyUnit.position); return { playerUnit, enemyUnit }; } /** * Cleans up turn system state. * @param {GameLoop} gameLoop */ export function cleanupTurnSystem(gameLoop) { if (gameLoop.turnSystem) { try { // First, try to end combat immediately to stop any ongoing turn advancement if ( gameLoop.turnSystem.phase !== "INIT" && gameLoop.turnSystem.phase !== "COMBAT_END" ) { // End combat first to stop any loops gameLoop.turnSystem.endCombat(); } // Then reset the turn system if (typeof gameLoop.turnSystem.reset === "function") { gameLoop.turnSystem.reset(); } else { // Fallback: manually reset state gameLoop.turnSystem.globalTick = 0; gameLoop.turnSystem.activeUnitId = null; gameLoop.turnSystem.phase = "INIT"; gameLoop.turnSystem.round = 1; gameLoop.turnSystem.turnQueue = []; } } catch (e) { // Ignore errors during cleanup console.warn("Error during turn system cleanup:", e); } } }