feat: add optional positionHint to spawnNPC

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-07 13:36:37 +00:00
parent fa02a6e93e
commit 1576e6b717
2 changed files with 52 additions and 2 deletions
+48
View File
@@ -0,0 +1,48 @@
import { describe, it, expect } from 'vitest';
import { World } from '../../ecs/World.js';
import { GameMap } from '../../map/GameMap.js';
import { spawnNPC } from '../spawner.js';
import type { Position } from '@dflike/shared';
describe('spawnNPC', () => {
it('spawns at random position when no hint given', () => {
const world = new World();
const map = new GameMap();
const entity = spawnNPC(world, map);
const pos = world.getComponent<Position>(entity, 'position')!;
expect(pos).toBeDefined();
expect(pos.x).toBeGreaterThanOrEqual(0);
expect(pos.y).toBeGreaterThanOrEqual(0);
});
it('spawns at hint position when walkable', () => {
const world = new World();
const map = new GameMap();
const entity = spawnNPC(world, map, { x: 20, y: 20 });
const pos = world.getComponent<Position>(entity, 'position')!;
expect(pos).toEqual({ x: 20, y: 20 });
});
it('spawns at random position when hint is not walkable', () => {
const world = new World();
const map = new GameMap();
map.setObstacle(20, 20);
const entity = spawnNPC(world, map, { x: 20, y: 20 });
const pos = world.getComponent<Position>(entity, 'position')!;
expect(pos).toBeDefined();
expect(pos.x !== 20 || pos.y !== 20).toBe(true);
});
it('creates all required components', () => {
const world = new World();
const map = new GameMap();
const entity = spawnNPC(world, map);
expect(world.getComponent(entity, 'position')).toBeDefined();
expect(world.getComponent(entity, 'needs')).toBeDefined();
expect(world.getComponent(entity, 'movement')).toBeDefined();
expect(world.getComponent(entity, 'npcBrain')).toBeDefined();
expect(world.getComponent(entity, 'appearance')).toBeDefined();
expect(world.getComponent(entity, 'name')).toBeDefined();
expect(world.getComponent(entity, 'socialState')).toBeDefined();
});
});
+4 -2
View File
@@ -4,9 +4,11 @@ import { generateRandomAppearance } from '../spawner/appearanceGenerator.js';
import { generateName } from '../spawner/nameGenerator.js';
import type { EntityId, Position, Needs, Movement, NPCBrain, Appearance, SocialState } from '@dflike/shared';
export function spawnNPC(world: World, map: GameMap): EntityId {
export function spawnNPC(world: World, map: GameMap, positionHint?: Position): EntityId {
const entity = world.createEntity();
const pos = map.getRandomWalkable();
const pos = positionHint && map.isWalkable(positionHint.x, positionHint.y)
? positionHint
: map.getRandomWalkable();
world.addComponent<Position>(entity, 'position', pos);
world.addComponent<Needs>(entity, 'needs', {