Add comprehensive tests for the InventoryManager and InventoryContainer to validate item management functionalities. Implement integration tests for the CharacterSheet component, ensuring proper interaction with the inventory system. Update the Explorer class to support new inventory features and maintain backward compatibility. Refactor related components for improved clarity and performance.
160 lines
5.4 KiB
JavaScript
160 lines
5.4 KiB
JavaScript
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();
|
|
});
|
|
});
|