aether-shards/test/core/GameLoop/deployment.test.js

161 lines
5.4 KiB
JavaScript
Raw Normal View History

import { expect } from "@esm-bundle/chai";
import sinon from "sinon";
import { GameLoop } from "../../../src/core/GameLoop.js";
import {
createGameLoopSetup,
cleanupGameLoop,
createRunData,
createMockGameStateManagerForDeployment,
createMockMissionManager,
} from "./helpers.js";
describe("Core: GameLoop - Deployment", function () {
this.timeout(30000);
let gameLoop;
let container;
beforeEach(() => {
const setup = createGameLoopSetup();
gameLoop = setup.gameLoop;
container = setup.container;
gameLoop.init(container);
});
afterEach(() => {
cleanupGameLoop(gameLoop, container);
});
it("CoA 3: Deployment Phase should separate zones and allow manual placement", async () => {
const runData = createRunData({
squad: [{ id: "u1", classId: "CLASS_VANGUARD" }],
});
// Mock gameStateManager for deployment phase
gameLoop.gameStateManager = createMockGameStateManagerForDeployment();
// startLevel should now prepare the map but NOT spawn units immediately
await gameLoop.startLevel(runData, { startAnimation: false });
// 1. Verify Spawn Zones Generated
// The generator/loop should identify valid tiles for player start and enemy start
expect(gameLoop.playerSpawnZone).to.be.an("array").that.is.not.empty;
expect(gameLoop.enemySpawnZone).to.be.an("array").that.is.not.empty;
// 2. Verify Zone Separation
// Create copies to ensure we don't test against mutated arrays later
const pZone = [...gameLoop.playerSpawnZone];
const eZone = [...gameLoop.enemySpawnZone];
const overlap = pZone.some((pTile) =>
eZone.some((eTile) => eTile.x === pTile.x && eTile.z === pTile.z)
);
expect(overlap).to.be.false;
// 3. Test Manual Deployment (User Selection)
const unitDef = runData.squad[0];
const validTile = pZone[0]; // Pick first valid tile from player zone
// Expect a method to manually place a unit from the roster onto a specific tile
const unit = gameLoop.deployUnit(unitDef, validTile);
expect(unit).to.exist;
expect(unit.position.x).to.equal(validTile.x);
expect(unit.position.z).to.equal(validTile.z);
// Verify visual mesh created
const mesh = gameLoop.unitMeshes.get(unit.id);
expect(mesh).to.exist;
expect(mesh.position.x).to.equal(validTile.x);
// 4. Test Enemy Spawning (Finalize Deployment)
// This triggers the actual start of combat/AI
gameLoop.finalizeDeployment();
const enemies = gameLoop.unitManager.getUnitsByTeam("ENEMY");
expect(enemies.length).to.be.greaterThan(0);
// Verify enemies are in their zone
// Note: finalizeDeployment removes used spots from gameLoop.enemySpawnZone,
// so we check against our copy `eZone`.
const enemyPos = enemies[0].position;
const isInZone = eZone.some(
(t) => t.x === enemyPos.x && t.z === enemyPos.z
);
expect(
isInZone,
`Enemy spawned at ${enemyPos.x},${enemyPos.z} which is not in enemy zone`
).to.be.true;
});
it("CoA 5: finalizeDeployment should spawn enemies from mission enemy_spawns", async () => {
const runData = createRunData({
squad: [{ id: "u1", classId: "CLASS_VANGUARD" }],
});
// Mock gameStateManager for deployment phase
gameLoop.gameStateManager = createMockGameStateManagerForDeployment();
// Mock MissionManager with enemy_spawns
// Use ENEMY_DEFAULT which exists in the test environment
gameLoop.missionManager = createMockMissionManager([
{ enemy_def_id: "ENEMY_DEFAULT", count: 2 },
]);
await gameLoop.startLevel(runData, { startAnimation: false });
// Copy enemy spawn zone before finalizeDeployment modifies it
const eZone = [...gameLoop.enemySpawnZone];
// Finalize deployment should spawn enemies from mission definition
gameLoop.finalizeDeployment();
const enemies = gameLoop.unitManager.getUnitsByTeam("ENEMY");
// Should have spawned 2 enemies (or as many as possible given spawn zone size)
expect(enemies.length).to.be.greaterThan(0);
expect(enemies.length).to.be.at.most(2);
// Verify enemies are in their zone
enemies.forEach((enemy) => {
const enemyPos = enemy.position;
const isInZone = eZone.some(
(t) => t.x === enemyPos.x && t.z === enemyPos.z
);
expect(
isInZone,
`Enemy spawned at ${enemyPos.x},${enemyPos.z} which is not in enemy zone`
).to.be.true;
});
});
it("CoA 6: finalizeDeployment should fall back to default if no enemy_spawns", async () => {
const runData = createRunData({
squad: [{ id: "u1", classId: "CLASS_VANGUARD" }],
});
// Mock gameStateManager for deployment phase
gameLoop.gameStateManager = createMockGameStateManagerForDeployment();
// Mock MissionManager with no enemy_spawns
gameLoop.missionManager = createMockMissionManager([]);
await gameLoop.startLevel(runData, { startAnimation: false });
// Finalize deployment should fall back to default behavior
const consoleWarnSpy = sinon.spy(console, "warn");
gameLoop.finalizeDeployment();
// Should have warned about missing enemy_spawns
expect(consoleWarnSpy.calledWith(sinon.match(/No enemy_spawns defined/))).to
.be.true;
const enemies = gameLoop.unitManager.getUnitsByTeam("ENEMY");
// Should still spawn at least one enemy (default behavior)
expect(enemies.length).to.be.greaterThan(0);
consoleWarnSpy.restore();
});
});