✨ add F3 debug view (Issue #6)
F3 toggles a debug overlay with: - FPS - Mouse world/tile coordinates - Tile type under cursor - Resources, buildings, crops on hovered tile - Nisse count broken down by AI state (idle/walking/working/sleeping) - Active jobs by type (chop/mine/farm) - Pathfinding visualization: cyan lines + destination highlight drawn in world space via DebugSystem Added DebugSystem to GameScene. VillagerSystem exposes getActivePaths() for the path visualization. JSDoc added to all previously undocumented methods in VillagerSystem, WorldSystem, GameScene, and UIScene.
This commit is contained in:
164
src/systems/DebugSystem.ts
Normal file
164
src/systems/DebugSystem.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,18 +36,32 @@ export class VillagerSystem {
|
||||
|
||||
onMessage?: (msg: string) => void
|
||||
|
||||
/**
|
||||
* @param scene - The Phaser scene this system belongs to
|
||||
* @param adapter - Network adapter for dispatching state actions
|
||||
* @param worldSystem - Used for passability checks during pathfinding
|
||||
*/
|
||||
constructor(scene: Phaser.Scene, adapter: LocalAdapter, worldSystem: WorldSystem) {
|
||||
this.scene = scene
|
||||
this.adapter = adapter
|
||||
this.worldSystem = worldSystem
|
||||
}
|
||||
|
||||
/** Wire in sibling systems after construction */
|
||||
/**
|
||||
* Wires in sibling systems that are not available at construction time.
|
||||
* Must be called before create().
|
||||
* @param resourceSystem - Used to remove harvested resource sprites
|
||||
* @param farmingSystem - Used to remove harvested crop sprites
|
||||
*/
|
||||
init(resourceSystem: ResourceSystem, farmingSystem: FarmingSystem): void {
|
||||
this.resourceSystem = resourceSystem
|
||||
this.farmingSystem = farmingSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns sprites for all Nisse that exist in the saved state
|
||||
* and re-claims any active job targets.
|
||||
*/
|
||||
create(): void {
|
||||
const state = stateManager.getState()
|
||||
for (const v of Object.values(state.world.villagers)) {
|
||||
@@ -57,6 +71,10 @@ export class VillagerSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the spawn timer and ticks every Nisse's AI.
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
update(delta: number): void {
|
||||
this.spawnTimer += delta
|
||||
if (this.spawnTimer >= VILLAGER_SPAWN_INTERVAL) {
|
||||
@@ -72,6 +90,12 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Per-villager tick ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Dispatches the correct AI tick method based on the villager's current state,
|
||||
* then syncs the sprite, name label, energy bar, and job icon to the state.
|
||||
* @param v - Villager state from the store
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
private tickVillager(v: VillagerState, delta: number): void {
|
||||
const rt = this.runtime.get(v.id)
|
||||
if (!rt) return
|
||||
@@ -97,6 +121,14 @@ export class VillagerSystem {
|
||||
|
||||
// ─── IDLE ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Handles the idle AI state: hauls items to stockpile if carrying any,
|
||||
* seeks a bed if energy is low, otherwise picks the next job and begins walking.
|
||||
* Applies a cooldown before scanning again if no job is found.
|
||||
* @param v - Villager state
|
||||
* @param rt - Villager runtime (sprites, path, timers)
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
private tickIdle(v: VillagerState, rt: VillagerRuntime, delta: number): void {
|
||||
// Decrement scan timer if cooling down
|
||||
if (rt.idleScanTimer > 0) {
|
||||
@@ -133,6 +165,14 @@ export class VillagerSystem {
|
||||
|
||||
// ─── WALKING ──────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Advances the Nisse along its path toward the current destination.
|
||||
* Calls onArrived when the path is exhausted.
|
||||
* Drains energy slowly while walking.
|
||||
* @param v - Villager state
|
||||
* @param rt - Villager runtime
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
private tickWalking(v: VillagerState, rt: VillagerRuntime, delta: number): void {
|
||||
if (rt.path.length === 0) {
|
||||
this.onArrived(v, rt)
|
||||
@@ -161,6 +201,12 @@ export class VillagerSystem {
|
||||
;(v as { energy: number }).energy = Math.max(0, v.energy - delta * 0.0015)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a Nisse reaches its destination tile.
|
||||
* Transitions to the appropriate next AI state based on destination type.
|
||||
* @param v - Villager state
|
||||
* @param rt - Villager runtime
|
||||
*/
|
||||
private onArrived(v: VillagerState, rt: VillagerRuntime): void {
|
||||
switch (rt.destination) {
|
||||
case 'job':
|
||||
@@ -186,6 +232,14 @@ export class VillagerSystem {
|
||||
|
||||
// ─── WORKING ──────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Counts down the work timer and performs the harvest action on completion.
|
||||
* Handles chop, mine, and farm job types.
|
||||
* Returns the Nisse to idle when done.
|
||||
* @param v - Villager state
|
||||
* @param rt - Villager runtime
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
private tickWorking(v: VillagerState, rt: VillagerRuntime, delta: number): void {
|
||||
rt.workTimer -= delta
|
||||
// Wobble while working
|
||||
@@ -237,6 +291,12 @@ export class VillagerSystem {
|
||||
|
||||
// ─── SLEEPING ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Restores energy while sleeping. Returns to idle once energy is full.
|
||||
* @param v - Villager state
|
||||
* @param rt - Villager runtime
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
private tickSleeping(v: VillagerState, rt: VillagerRuntime, delta: number): void {
|
||||
;(v as { energy: number }).energy = Math.min(100, v.energy + delta * 0.04)
|
||||
// Gentle bob while sleeping
|
||||
@@ -249,6 +309,13 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Job picking (RimWorld-style priority) ────────────────────────────────
|
||||
|
||||
/**
|
||||
* Selects the best available job for a Nisse based on their priority settings.
|
||||
* Among jobs at the same priority level, the closest one wins.
|
||||
* Returns null if no unclaimed job is available.
|
||||
* @param v - Villager state (used for position and priorities)
|
||||
* @returns The chosen job candidate, or null
|
||||
*/
|
||||
private pickJob(v: VillagerState): { type: JobType; targetId: string; tileX: number; tileY: number } | null {
|
||||
const state = stateManager.getState()
|
||||
const p = v.priorities
|
||||
@@ -289,6 +356,15 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Pathfinding ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Computes a path from the Nisse's current tile to the target tile and
|
||||
* begins walking. If no path is found, the job is cleared and a cooldown applied.
|
||||
* @param v - Villager state
|
||||
* @param rt - Villager runtime
|
||||
* @param tileX - Target tile X
|
||||
* @param tileY - Target tile Y
|
||||
* @param dest - Semantic destination type (used by onArrived)
|
||||
*/
|
||||
private beginWalk(v: VillagerState, rt: VillagerRuntime, tileX: number, tileY: number, dest: VillagerRuntime['destination']): void {
|
||||
const sx = Math.floor(v.x / TILE_SIZE)
|
||||
const sy = Math.floor(v.y / TILE_SIZE)
|
||||
@@ -310,6 +386,11 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Building finders ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns the nearest building of the given kind to the Nisse, or null if none exist.
|
||||
* @param v - Villager state (used as reference position)
|
||||
* @param kind - Building kind to search for
|
||||
*/
|
||||
private nearestBuilding(v: VillagerState, kind: string): { tileX: number; tileY: number } | null {
|
||||
const state = stateManager.getState()
|
||||
const hits = Object.values(state.world.buildings).filter(b => b.kind === kind)
|
||||
@@ -319,6 +400,11 @@ export class VillagerSystem {
|
||||
return hits.sort((a, b) => Math.hypot(a.tileX - vx, a.tileY - vy) - Math.hypot(b.tileX - vx, b.tileY - vy))[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Nisse's assigned bed if it still exists, otherwise the nearest bed.
|
||||
* Returns null if no beds are placed.
|
||||
* @param v - Villager state
|
||||
*/
|
||||
private findBed(v: VillagerState): { id: string; tileX: number; tileY: number } | null {
|
||||
const state = stateManager.getState()
|
||||
// Prefer assigned bed
|
||||
@@ -328,6 +414,10 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Spawning ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Attempts to spawn a new Nisse if a free bed is available and the
|
||||
* current population is below the bed count.
|
||||
*/
|
||||
private trySpawn(): void {
|
||||
const state = stateManager.getState()
|
||||
const beds = Object.values(state.world.buildings).filter(b => b.kind === 'bed')
|
||||
@@ -361,6 +451,11 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Sprite management ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Creates and registers all runtime objects (sprite, label, energy bar, icon)
|
||||
* for a newly added Nisse.
|
||||
* @param v - Villager state to create sprites for
|
||||
*/
|
||||
private spawnSprite(v: VillagerState): void {
|
||||
const sprite = this.scene.add.image(v.x, v.y, 'villager').setDepth(11)
|
||||
|
||||
@@ -375,6 +470,14 @@ export class VillagerSystem {
|
||||
this.runtime.set(v.id, { sprite, nameLabel, energyBar, jobIcon, path: [], destination: null, workTimer: 0, idleScanTimer: 0 })
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraws the energy bar graphic for a Nisse at the given world position.
|
||||
* Color transitions green → orange → red as energy decreases.
|
||||
* @param g - Graphics object to draw into
|
||||
* @param x - World X center of the Nisse
|
||||
* @param y - World Y center of the Nisse
|
||||
* @param energy - Current energy value (0–100)
|
||||
*/
|
||||
private drawEnergyBar(g: Phaser.GameObjects.Graphics, x: number, y: number, energy: number): void {
|
||||
const W = 20, H = 3
|
||||
g.clear()
|
||||
@@ -385,6 +488,12 @@ export class VillagerSystem {
|
||||
|
||||
// ─── Public API ───────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns a short human-readable status string for the given Nisse,
|
||||
* suitable for display in UI panels.
|
||||
* @param villagerId - The Nisse's ID
|
||||
* @returns Status string, or '—' if the Nisse is not found
|
||||
*/
|
||||
getStatusText(villagerId: string): string {
|
||||
const v = stateManager.getState().world.villagers[villagerId]
|
||||
if (!v) return '—'
|
||||
@@ -397,6 +506,28 @@ export class VillagerSystem {
|
||||
return '💭 Idle'
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current world position and remaining path for every Nisse
|
||||
* that is currently in the 'walking' state. Used by DebugSystem for
|
||||
* pathfinding visualization.
|
||||
* @returns Array of path entries, one per walking Nisse
|
||||
*/
|
||||
getActivePaths(): Array<{ x: number; y: number; path: Array<{ tileX: number; tileY: number }> }> {
|
||||
const state = stateManager.getState()
|
||||
const result: Array<{ x: number; y: number; path: Array<{ tileX: number; tileY: number }> }> = []
|
||||
for (const v of Object.values(state.world.villagers)) {
|
||||
if (v.aiState !== 'walking') continue
|
||||
const rt = this.runtime.get(v.id)
|
||||
if (!rt) continue
|
||||
result.push({ x: v.x, y: v.y, path: [...rt.path] })
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all Nisse sprites and clears the runtime map.
|
||||
* Should be called when the scene shuts down.
|
||||
*/
|
||||
destroy(): void {
|
||||
for (const rt of this.runtime.values()) {
|
||||
rt.sprite.destroy(); rt.nameLabel.destroy()
|
||||
|
||||
@@ -22,10 +22,15 @@ export class WorldSystem {
|
||||
private bgImage!: Phaser.GameObjects.Image
|
||||
private builtLayer!: Phaser.Tilemaps.TilemapLayer
|
||||
|
||||
/** @param scene - The Phaser scene this system belongs to */
|
||||
constructor(scene: Phaser.Scene) {
|
||||
this.scene = scene
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the terrain background canvas from saved tile data,
|
||||
* creates the built-tile tilemap layer, and sets camera bounds.
|
||||
*/
|
||||
create(): void {
|
||||
const state = stateManager.getState()
|
||||
|
||||
@@ -81,10 +86,18 @@ export class WorldSystem {
|
||||
this.scene.cameras.main.setBounds(0, 0, WORLD_TILES * TILE_SIZE, WORLD_TILES * TILE_SIZE)
|
||||
}
|
||||
|
||||
/** Returns the built-tile tilemap layer (floor, wall, soil). */
|
||||
getLayer(): Phaser.Tilemaps.TilemapLayer {
|
||||
return this.builtLayer
|
||||
}
|
||||
|
||||
/**
|
||||
* Places or removes a tile on the built layer.
|
||||
* Built tile types are added; natural types remove the built-layer entry.
|
||||
* @param tileX - Tile column
|
||||
* @param tileY - Tile row
|
||||
* @param type - New tile type to apply
|
||||
*/
|
||||
setTile(tileX: number, tileY: number, type: TileType): void {
|
||||
const BUILT_TILES = new Set([TileType.FLOOR, TileType.WALL, TileType.TILLED_SOIL, TileType.WATERED_SOIL])
|
||||
if (BUILT_TILES.has(type)) {
|
||||
@@ -95,6 +108,12 @@ export class WorldSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the tile at the given coordinates can be walked on.
|
||||
* Out-of-bounds tiles are treated as impassable.
|
||||
* @param tileX - Tile column
|
||||
* @param tileY - Tile row
|
||||
*/
|
||||
isPassable(tileX: number, tileY: number): boolean {
|
||||
if (tileX < 0 || tileY < 0 || tileX >= WORLD_TILES || tileY >= WORLD_TILES) return false
|
||||
const state = stateManager.getState()
|
||||
@@ -102,6 +121,12 @@ export class WorldSystem {
|
||||
return !IMPASSABLE.has(tile)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts world pixel coordinates to tile coordinates.
|
||||
* @param worldX - World X in pixels
|
||||
* @param worldY - World Y in pixels
|
||||
* @returns Integer tile position
|
||||
*/
|
||||
worldToTile(worldX: number, worldY: number): { tileX: number; tileY: number } {
|
||||
return {
|
||||
tileX: Math.floor(worldX / TILE_SIZE),
|
||||
@@ -109,6 +134,12 @@ export class WorldSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts tile coordinates to the world pixel center of that tile.
|
||||
* @param tileX - Tile column
|
||||
* @param tileY - Tile row
|
||||
* @returns World pixel center position
|
||||
*/
|
||||
tileToWorld(tileX: number, tileY: number): { x: number; y: number } {
|
||||
return {
|
||||
x: tileX * TILE_SIZE + TILE_SIZE / 2,
|
||||
@@ -116,11 +147,17 @@ export class WorldSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile type at the given tile coordinates from saved state.
|
||||
* @param tileX - Tile column
|
||||
* @param tileY - Tile row
|
||||
*/
|
||||
getTileType(tileX: number, tileY: number): TileType {
|
||||
const state = stateManager.getState()
|
||||
return state.world.tiles[tileY * WORLD_TILES + tileX] as TileType
|
||||
}
|
||||
|
||||
/** Destroys the tilemap and background image. */
|
||||
destroy(): void {
|
||||
this.map.destroy()
|
||||
this.bgImage.destroy()
|
||||
|
||||
Reference in New Issue
Block a user