Files
dflike/server/src/map/GameMap.ts
T
root 6f459fa88a feat: add trunk decorations, resize superlatives panel, add LLM plan docs
- Add trunk decoration layer to GameMap and mapGenerator
- Load trunk.png asset in BootScene
- Increase superlatives panel size and font sizes for readability
- Simplify mapGenerator (remove procedural terrain features)
- Add LLM integration planning documents
- Remove stale day-night cycle worktree plans
- Update local Claude settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:09:19 +00:00

85 lines
2.3 KiB
TypeScript

import { WORLD_WIDTH, WORLD_HEIGHT, type Position } from '@dflike/shared';
export interface PointOfInterest {
type: 'food' | 'rest';
position: Position;
}
export class GameMap {
readonly width: number;
readonly height: number;
private obstacles: Set<string> = new Set();
private pois: PointOfInterest[] = [];
terrain: number[] = []; // flat array of Terrain values
decorations: number[] = []; // flat array of canopy tile indices (-1 = none)
trunkDecorations: number[] = []; // flat array of trunk tile indices (-1 = none)
constructor(width = WORLD_WIDTH, height = WORLD_HEIGHT) {
this.width = width;
this.height = height;
}
private key(x: number, y: number): string {
return `${x},${y}`;
}
setObstacle(x: number, y: number): void {
this.obstacles.add(this.key(x, y));
}
loadObstacles(obstacleSet: Set<string>): void {
for (const key of obstacleSet) {
this.obstacles.add(key);
}
}
isWalkable(x: number, y: number): boolean {
if (x < 0 || y < 0 || x >= this.width || y >= this.height) return false;
return !this.obstacles.has(this.key(x, y));
}
addPointOfInterest(poi: PointOfInterest): void {
this.pois.push(poi);
}
getPointsOfInterest(type?: 'food' | 'rest'): PointOfInterest[] {
if (type) return this.pois.filter(p => p.type === type);
return this.pois;
}
getObstacles(): Position[] {
return [...this.obstacles].map(k => {
const [x, y] = k.split(',').map(Number);
return { x, y };
});
}
findNearestWalkable(centerX: number, centerY: number, radius: number): Position | null {
if (this.isWalkable(centerX, centerY)) {
return { x: centerX, y: centerY };
}
for (let dist = 1; dist <= radius; dist++) {
for (let dx = -dist; dx <= dist; dx++) {
const dyRange = dist - Math.abs(dx);
for (const dy of [-dyRange, dyRange]) {
const x = centerX + dx;
const y = centerY + dy;
if (this.isWalkable(x, y)) {
return { x, y };
}
}
}
}
return null;
}
getRandomWalkable(): Position {
let x: number, y: number;
do {
x = Math.floor(Math.random() * this.width);
y = Math.floor(Math.random() * this.height);
} while (!this.isWalkable(x, y));
return { x, y };
}
}