Fixes #31. All trees, rocks, seedlings and buildings now use tileY+5 as depth instead of a fixed value, so objects further down the screen always render in front of objects above them regardless of spawn order. Build ghost moved to depth 1000/1001. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
import Phaser from 'phaser'
|
|
import { TILE_SIZE } from '../config'
|
|
import { TileType } from '../types'
|
|
import { stateManager } from '../StateManager'
|
|
import type { LocalAdapter } from '../NetworkAdapter'
|
|
import type { ResourceNodeState } from '../types'
|
|
|
|
interface ResourceSprite {
|
|
sprite: Phaser.GameObjects.Image
|
|
node: ResourceNodeState
|
|
healthBar: Phaser.GameObjects.Graphics
|
|
}
|
|
|
|
export class ResourceSystem {
|
|
private scene: Phaser.Scene
|
|
private adapter: LocalAdapter
|
|
private sprites = new Map<string, ResourceSprite>()
|
|
|
|
// Emits after each successful harvest: (message: string)
|
|
onHarvest?: (message: string) => void
|
|
|
|
constructor(scene: Phaser.Scene, adapter: LocalAdapter) {
|
|
this.scene = scene
|
|
this.adapter = adapter
|
|
}
|
|
|
|
create(): void {
|
|
const state = stateManager.getState()
|
|
|
|
// Spawn sprites for all resources in state
|
|
for (const node of Object.values(state.world.resources)) {
|
|
this.spawnSprite(node)
|
|
}
|
|
}
|
|
|
|
private spawnSprite(node: ResourceNodeState): void {
|
|
const x = (node.tileX + 0.5) * TILE_SIZE
|
|
const y = (node.tileY + 0.5) * TILE_SIZE
|
|
|
|
const key = node.kind === 'tree' ? 'tree' : 'rock'
|
|
const sprite = this.scene.add.image(x, y, key)
|
|
|
|
// Trees are taller; offset them upward so trunk sits on tile
|
|
if (node.kind === 'tree') {
|
|
sprite.setOrigin(0.5, 0.85)
|
|
} else {
|
|
sprite.setOrigin(0.5, 0.75)
|
|
}
|
|
|
|
sprite.setDepth(node.tileY + 5)
|
|
|
|
const healthBar = this.scene.add.graphics()
|
|
healthBar.setDepth(node.tileY + 6)
|
|
healthBar.setVisible(false)
|
|
|
|
this.sprites.set(node.id, { sprite, node, healthBar })
|
|
}
|
|
|
|
update(delta: number): void {
|
|
// Hide all health bars each frame (no player proximity detection)
|
|
for (const entry of this.sprites.values()) {
|
|
entry.healthBar.setVisible(false)
|
|
}
|
|
}
|
|
|
|
private removeSprite(id: string): void {
|
|
const entry = this.sprites.get(id)
|
|
if (!entry) return
|
|
entry.sprite.destroy()
|
|
entry.healthBar.destroy()
|
|
this.sprites.delete(id)
|
|
}
|
|
|
|
/** Called by VillagerSystem when a villager finishes chopping/mining */
|
|
public removeResource(id: string): void {
|
|
this.removeSprite(id)
|
|
}
|
|
|
|
/**
|
|
* Spawns a sprite for a resource that was created at runtime
|
|
* (e.g. a tree grown from a seedling). The resource must already be
|
|
* present in the game state when this is called.
|
|
* @param node - The resource node to render
|
|
*/
|
|
public spawnResourcePublic(node: ResourceNodeState): void {
|
|
this.spawnSprite(node)
|
|
}
|
|
|
|
/** Called when WorldSystem changes a tile (e.g. after tree removed) */
|
|
syncTileChange(tileX: number, tileY: number, worldSystem: { setTile: (x: number, y: number, type: TileType) => void }): void {
|
|
const state = stateManager.getState()
|
|
const idx = tileY * 512 + tileX // WORLD_TILES = 512
|
|
const tile = state.world.tiles[idx] as TileType
|
|
worldSystem.setTile(tileX, tileY, tile)
|
|
}
|
|
|
|
destroy(): void {
|
|
for (const [id] of this.sprites) this.removeSprite(id)
|
|
}
|
|
}
|