test(systems): add industry integration tests for gather/craft/build lifecycle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { World } from '../../ecs/World.js';
|
||||
import { GameMap } from '../../map/GameMap.js';
|
||||
import { industrySystem } from '../industrySystem.js';
|
||||
import { craftingSystem, type CraftingState } from '../craftingSystem.js';
|
||||
import { buildingSystem, type BuildingState, type StructureData } from '../buildingSystem.js';
|
||||
import { gatheringSystem, type GatheringState } from '../gatheringSystem.js';
|
||||
import { dropoffSystem } from '../dropoffSystem.js';
|
||||
import { RecipeRegistry } from '../../industry/recipeRegistry.js';
|
||||
import type { Needs, Movement, NPCBrain, Position, Stats, StatModifiers } from '@dflike/shared';
|
||||
|
||||
function createTestWorld(): { world: World; map: GameMap } {
|
||||
const world = new World();
|
||||
world.setSingleton('recipeRegistry', RecipeRegistry.createDefault());
|
||||
const map = new GameMap(20, 20);
|
||||
map.resourceTiles = [
|
||||
{ x: 3, y: 3, resourceType: 'log' },
|
||||
{ x: 7, y: 7, resourceType: 'stone' },
|
||||
];
|
||||
return { world, map };
|
||||
}
|
||||
|
||||
function createTestNPC(world: World, x: number, y: number, inv?: Map<string, number>) {
|
||||
const e = world.createEntity();
|
||||
world.addComponent<Position>(e, 'position', { x, y });
|
||||
world.addComponent<Needs>(e, 'needs', { hunger: 80, energy: 80, productivity: 20 });
|
||||
world.addComponent<Movement>(e, 'movement', { state: 'idle', target: null, path: [], direction: 0, moveProgress: 0 });
|
||||
world.addComponent<NPCBrain>(e, 'npcBrain', { currentGoal: 'wander', goalQueue: [] });
|
||||
world.addComponent<Map<string, number>>(e, 'inventory', inv ?? new Map());
|
||||
world.addComponent<Stats>(e, 'stats', {
|
||||
strength: 10, dexterity: 10, constitution: 10, intelligence: 10, perception: 10,
|
||||
sociability: 10, courage: 10, curiosity: 10, empathy: 10, temperament: 10,
|
||||
});
|
||||
world.addComponent<StatModifiers>(e, 'statModifiers', { modifiers: [] });
|
||||
return e;
|
||||
}
|
||||
|
||||
describe('industry integration', () => {
|
||||
it('NPC with materials crafts a tool', () => {
|
||||
const { world, map } = createTestWorld();
|
||||
const inv = new Map([['log', 4], ['stone', 2]]);
|
||||
const e = createTestNPC(world, 5, 5, inv);
|
||||
|
||||
industrySystem(world, map);
|
||||
const brain = world.getComponent<NPCBrain>(e, 'npcBrain')!;
|
||||
expect(brain.currentGoal).toBe('craft');
|
||||
|
||||
const cs = world.getComponent<CraftingState>(e, 'craftingState')!;
|
||||
const ticks = cs.ticksRemaining;
|
||||
for (let i = 0; i < ticks + 1; i++) {
|
||||
craftingSystem(world);
|
||||
}
|
||||
|
||||
const finalInv = world.getComponent<Map<string, number>>(e, 'inventory')!;
|
||||
expect(finalInv.has('wooden_axe') || finalInv.has('hammer')).toBe(true);
|
||||
});
|
||||
|
||||
it('NPC builds a stockpile when it has materials and no stockpile exists', () => {
|
||||
const { world, map } = createTestWorld();
|
||||
const inv = new Map([['log', 4]]);
|
||||
const e = createTestNPC(world, 5, 5, inv);
|
||||
|
||||
industrySystem(world, map);
|
||||
const brain = world.getComponent<NPCBrain>(e, 'npcBrain')!;
|
||||
expect(brain.currentGoal).toBe('build');
|
||||
|
||||
const bs = world.getComponent<BuildingState>(e, 'buildingState')!;
|
||||
for (let i = 0; i < 60; i++) {
|
||||
buildingSystem(world, map);
|
||||
}
|
||||
|
||||
const structure = world.getComponent<StructureData>(bs.structureEntityId, 'structure')!;
|
||||
expect(structure.buildProgress).toBeUndefined();
|
||||
});
|
||||
|
||||
it('NPC drops off items at stockpile after gathering', () => {
|
||||
const { world, map } = createTestWorld();
|
||||
const e = createTestNPC(world, 5, 5, new Map([['log', 2]]));
|
||||
|
||||
const stockpileEntity = world.createEntity();
|
||||
world.addComponent<Position>(stockpileEntity, 'position', { x: 5, y: 5 });
|
||||
world.addComponent<StructureData>(stockpileEntity, 'structure', {
|
||||
type: 'stockpile',
|
||||
subtype: 'general',
|
||||
inventory: new Map(),
|
||||
});
|
||||
|
||||
const brain = world.getComponent<NPCBrain>(e, 'npcBrain')!;
|
||||
brain.currentGoal = 'dropoff';
|
||||
|
||||
dropoffSystem(world, map);
|
||||
|
||||
const npcInv = world.getComponent<Map<string, number>>(e, 'inventory')!;
|
||||
expect(npcInv.size).toBe(0);
|
||||
|
||||
const stockpile = world.getComponent<StructureData>(stockpileEntity, 'structure')!;
|
||||
expect(stockpile.inventory.get('log')).toBe(2);
|
||||
expect(brain.currentGoal).toBe('wander');
|
||||
});
|
||||
|
||||
it('full lifecycle: no materials → gather fallback', () => {
|
||||
const { world, map } = createTestWorld();
|
||||
const e = createTestNPC(world, 5, 5);
|
||||
|
||||
industrySystem(world, map);
|
||||
const brain = world.getComponent<NPCBrain>(e, 'npcBrain')!;
|
||||
expect(brain.currentGoal).toBe('gather');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user