Files
nissefolk/src/systems/DebugSystem.ts

165 lines
5.4 KiB
TypeScript
Raw Normal View History

import Phaser from 'phaser'
import { TILE_SIZE } from '../config'
import { TileType } from '../types'
import { stateManager } from '../StateManager'
import type { VillagerSystem } from './VillagerSystem'
import type { WorldSystem } from './WorldSystem'
/** All data collected each frame for the debug panel. */
export interface DebugData {
fps: number
mouseWorld: { x: number; y: number }
mouseTile: { tileX: number; tileY: number }
tileType: string
resourcesOnTile: Array<{ kind: string; hp: number }>
buildingsOnTile: string[]
cropsOnTile: Array<{ kind: string; stage: number; maxStage: number }>
nisseTotal: number
nisseByState: { idle: number; walking: number; working: number; sleeping: number }
jobsByType: { chop: number; mine: number; farm: number }
activePaths: number
}
/** Human-readable names for TileType enum values. */
const TILE_NAMES: Record<number, string> = {
[TileType.DEEP_WATER]: 'DEEP_WATER',
[TileType.SHALLOW_WATER]: 'SHALLOW_WATER',
[TileType.SAND]: 'SAND',
[TileType.GRASS]: 'GRASS',
[TileType.DARK_GRASS]: 'DARK_GRASS',
[TileType.FOREST]: 'FOREST',
[TileType.ROCK]: 'ROCK',
[TileType.FLOOR]: 'FLOOR',
[TileType.WALL]: 'WALL',
[TileType.TILLED_SOIL]: 'TILLED_SOIL',
[TileType.WATERED_SOIL]: 'WATERED_SOIL',
}
export class DebugSystem {
private scene: Phaser.Scene
private villagerSystem: VillagerSystem
private worldSystem: WorldSystem
private pathGraphics!: Phaser.GameObjects.Graphics
private active = false
/**
* @param scene - The Phaser scene this system belongs to
* @param villagerSystem - Used to read active paths for visualization
* @param worldSystem - Used to read tile types under the mouse
*/
constructor(scene: Phaser.Scene, villagerSystem: VillagerSystem, worldSystem: WorldSystem) {
this.scene = scene
this.villagerSystem = villagerSystem
this.worldSystem = worldSystem
}
/**
* Creates the world-space Graphics object used for pathfinding visualization.
* Starts hidden until toggled on.
*/
create(): void {
this.pathGraphics = this.scene.add.graphics().setDepth(50)
this.pathGraphics.setVisible(false)
}
/**
* Toggles debug mode on or off.
* Shows or hides the pathfinding overlay graphics accordingly.
*/
toggle(): void {
this.active = !this.active
this.pathGraphics.setVisible(this.active)
if (!this.active) this.pathGraphics.clear()
}
/** Returns whether debug mode is currently active. */
isActive(): boolean {
return this.active
}
/**
* Redraws pathfinding lines for all currently walking Nisse.
* Should be called every frame while debug mode is active.
*/
update(): void {
if (!this.active) return
this.pathGraphics.clear()
const paths = this.villagerSystem.getActivePaths()
this.pathGraphics.lineStyle(1, 0x00ffff, 0.65)
for (const entry of paths) {
if (entry.path.length === 0) continue
this.pathGraphics.beginPath()
this.pathGraphics.moveTo(entry.x, entry.y)
for (const step of entry.path) {
this.pathGraphics.lineTo(
(step.tileX + 0.5) * TILE_SIZE,
(step.tileY + 0.5) * TILE_SIZE,
)
}
this.pathGraphics.strokePath()
// Mark the destination tile
const last = entry.path[entry.path.length - 1]
this.pathGraphics.fillStyle(0x00ffff, 0.4)
this.pathGraphics.fillRect(
last.tileX * TILE_SIZE,
last.tileY * TILE_SIZE,
TILE_SIZE,
TILE_SIZE,
)
}
}
/**
* Collects and returns all debug data for the current frame.
* Called by UIScene to populate the debug panel.
* @param ptr - The active pointer, used to resolve world position
* @returns Snapshot of game state for display
*/
getDebugData(ptr: Phaser.Input.Pointer): DebugData {
const state = stateManager.getState()
const villagers = Object.values(state.world.villagers)
const tileX = Math.floor(ptr.worldX / TILE_SIZE)
const tileY = Math.floor(ptr.worldY / TILE_SIZE)
const tileType = this.worldSystem.getTileType(tileX, tileY)
const nisseByState = { idle: 0, walking: 0, working: 0, sleeping: 0 }
const jobsByType = { chop: 0, mine: 0, farm: 0 }
for (const v of villagers) {
nisseByState[v.aiState as keyof typeof nisseByState]++
if (v.job && (v.aiState === 'working' || v.aiState === 'walking')) {
jobsByType[v.job.type as keyof typeof jobsByType]++
}
}
const resourcesOnTile = Object.values(state.world.resources)
.filter(r => r.tileX === tileX && r.tileY === tileY)
.map(r => ({ kind: r.kind, hp: r.hp }))
const buildingsOnTile = Object.values(state.world.buildings)
.filter(b => b.tileX === tileX && b.tileY === tileY)
.map(b => b.kind)
const cropsOnTile = Object.values(state.world.crops)
.filter(c => c.tileX === tileX && c.tileY === tileY)
.map(c => ({ kind: c.kind, stage: c.stage, maxStage: c.maxStage }))
return {
fps: Math.round(this.scene.game.loop.actualFps),
mouseWorld: { x: ptr.worldX, y: ptr.worldY },
mouseTile: { tileX, tileY },
tileType: TILE_NAMES[tileType] ?? `UNKNOWN(${tileType})`,
resourcesOnTile,
buildingsOnTile,
cropsOnTile,
nisseTotal: villagers.length,
nisseByState,
jobsByType,
activePaths: this.villagerSystem.getActivePaths().length,
}
}
}