ca05d36bc0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
import { TICK_RATE, BROADCAST_EVERY_N_TICKS } from '@dflike/shared';
|
|
import { World } from '../ecs/World.js';
|
|
import { GameMap } from '../map/GameMap.js';
|
|
import { needsDecaySystem } from '../systems/needsDecaySystem.js';
|
|
import { npcBrainSystem } from '../systems/npcBrainSystem.js';
|
|
import { movementSystem } from '../systems/movementSystem.js';
|
|
import { spawnNPC } from './spawner.js';
|
|
|
|
export class GameLoop {
|
|
readonly world: World;
|
|
readonly map: GameMap;
|
|
private tick = 0;
|
|
private interval: ReturnType<typeof setInterval> | null = null;
|
|
private onBroadcast: (() => void) | null = null;
|
|
|
|
constructor() {
|
|
this.world = new World();
|
|
this.map = new GameMap();
|
|
this.setupMap();
|
|
this.spawnInitialNPCs(8);
|
|
}
|
|
|
|
private setupMap(): void {
|
|
// Add some obstacle clusters for pathfinding interest
|
|
for (let x = 10; x <= 14; x++) for (let y = 10; y <= 12; y++) this.map.setObstacle(x, y);
|
|
for (let x = 30; x <= 33; x++) for (let y = 25; y <= 28; y++) this.map.setObstacle(x, y);
|
|
for (let x = 50; x <= 53; x++) for (let y = 40; y <= 43; y++) this.map.setObstacle(x, y);
|
|
|
|
// Points of interest
|
|
this.map.addPointOfInterest({ type: 'food', position: { x: 15, y: 15 } });
|
|
this.map.addPointOfInterest({ type: 'food', position: { x: 45, y: 30 } });
|
|
this.map.addPointOfInterest({ type: 'rest', position: { x: 8, y: 8 } });
|
|
this.map.addPointOfInterest({ type: 'rest', position: { x: 55, y: 50 } });
|
|
}
|
|
|
|
private spawnInitialNPCs(count: number): void {
|
|
for (let i = 0; i < count; i++) {
|
|
spawnNPC(this.world, this.map);
|
|
}
|
|
}
|
|
|
|
setBroadcastHandler(handler: () => void): void {
|
|
this.onBroadcast = handler;
|
|
}
|
|
|
|
start(): void {
|
|
const tickInterval = 1000 / TICK_RATE;
|
|
this.interval = setInterval(() => this.update(), tickInterval);
|
|
console.log(`Game loop started at ${TICK_RATE} ticks/sec`);
|
|
}
|
|
|
|
stop(): void {
|
|
if (this.interval) {
|
|
clearInterval(this.interval);
|
|
this.interval = null;
|
|
}
|
|
}
|
|
|
|
private update(): void {
|
|
this.tick++;
|
|
|
|
// Run systems in order
|
|
needsDecaySystem(this.world);
|
|
npcBrainSystem(this.world, this.map);
|
|
movementSystem(this.world);
|
|
|
|
// Broadcast state periodically
|
|
if (this.tick % BROADCAST_EVERY_N_TICKS === 0 && this.onBroadcast) {
|
|
this.onBroadcast();
|
|
}
|
|
}
|
|
|
|
getTick(): number {
|
|
return this.tick;
|
|
}
|
|
}
|