implement unified tile system (Issue #14)

- Tree seedlings: plant tree_seed on grass via farming tool; two-stage
  growth (sprout → sapling → young tree, ~1 min/stage); matures into
  a harvestable FOREST resource tile
- Tile recovery: Nisse chops start a 5-min DARK_GRASS→GRASS timer;
  terrain canvas updated live via WorldSystem.refreshTerrainTile()
- New TreeSeedlingSystem manages sprites, growth ticking, maturation
- BootScene generates seedling_0/1/2 textures procedurally
- FarmingSystem adds tree_seed to tool cycle (F key)
- Stockpile panel shows tree_seed (default: 5); panel height adjusted
- StateManager v5: treeSeedlings + tileRecovery in WorldState
- WorldSystem uses CanvasTexture for live single-pixel updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 16:15:21 +00:00
parent bbbb3e1f58
commit 18c8ccb644
12 changed files with 375 additions and 28 deletions

View File

@@ -1,5 +1,6 @@
import Phaser from 'phaser'
import { AUTOSAVE_INTERVAL, TILE_SIZE } from '../config'
import { TileType } from '../types'
import type { BuildingType } from '../types'
import { stateManager } from '../StateManager'
import { LocalAdapter } from '../NetworkAdapter'
@@ -10,6 +11,7 @@ import { BuildingSystem } from '../systems/BuildingSystem'
import { FarmingSystem } from '../systems/FarmingSystem'
import { VillagerSystem } from '../systems/VillagerSystem'
import { DebugSystem } from '../systems/DebugSystem'
import { TreeSeedlingSystem } from '../systems/TreeSeedlingSystem'
export class GameScene extends Phaser.Scene {
private adapter!: LocalAdapter
@@ -20,6 +22,7 @@ export class GameScene extends Phaser.Scene {
private farmingSystem!: FarmingSystem
villagerSystem!: VillagerSystem
debugSystem!: DebugSystem
private treeSeedlingSystem!: TreeSeedlingSystem
private autosaveTimer = 0
private menuOpen = false
@@ -37,9 +40,10 @@ export class GameScene extends Phaser.Scene {
this.resourceSystem = new ResourceSystem(this, this.adapter)
this.buildingSystem = new BuildingSystem(this, this.adapter)
this.farmingSystem = new FarmingSystem(this, this.adapter)
this.villagerSystem = new VillagerSystem(this, this.adapter, this.worldSystem)
this.villagerSystem = new VillagerSystem(this, this.adapter, this.worldSystem)
this.villagerSystem.init(this.resourceSystem, this.farmingSystem)
this.debugSystem = new DebugSystem(this, this.villagerSystem, this.worldSystem)
this.treeSeedlingSystem = new TreeSeedlingSystem(this, this.adapter, this.worldSystem)
this.debugSystem = new DebugSystem(this, this.villagerSystem, this.worldSystem)
this.worldSystem.create()
this.renderPersistentObjects()
@@ -57,8 +61,12 @@ export class GameScene extends Phaser.Scene {
}
this.farmingSystem.create()
this.farmingSystem.onMessage = (msg) => this.events.emit('toast', msg)
this.farmingSystem.onToolChange = (tool, label) => this.events.emit('farmToolChanged', tool, label)
this.farmingSystem.onMessage = (msg) => this.events.emit('toast', msg)
this.farmingSystem.onToolChange = (tool, label) => this.events.emit('farmToolChanged', tool, label)
this.farmingSystem.onPlantTreeSeed = (tileX, tileY, tile) =>
this.treeSeedlingSystem.plantSeedling(tileX, tileY, tile)
this.treeSeedlingSystem.create()
this.villagerSystem.create()
this.villagerSystem.onMessage = (msg) => this.events.emit('toast', msg)
@@ -70,6 +78,8 @@ export class GameScene extends Phaser.Scene {
this.adapter.onAction = (action) => {
if (action.type === 'CHANGE_TILE') {
this.worldSystem.setTile(action.tileX, action.tileY, action.tile)
} else if (action.type === 'SPAWN_RESOURCE') {
this.resourceSystem.spawnResourcePublic(action.resource)
}
}
@@ -102,9 +112,17 @@ export class GameScene extends Phaser.Scene {
this.resourceSystem.update(delta)
this.farmingSystem.update(delta)
this.treeSeedlingSystem.update(delta)
this.villagerSystem.update(delta)
this.debugSystem.update()
// Tick tile-recovery timers; refresh canvas for any tiles that reverted to GRASS
const recovered = stateManager.tickTileRecovery(delta)
for (const key of recovered) {
const [tx, ty] = key.split(',').map(Number)
this.worldSystem.refreshTerrainTile(tx, ty, TileType.GRASS)
}
this.events.emit('cameraMoved', this.cameraSystem.getCenterTile())
this.buildingSystem.update()
@@ -144,6 +162,7 @@ export class GameScene extends Phaser.Scene {
this.resourceSystem.destroy()
this.buildingSystem.destroy()
this.farmingSystem.destroy()
this.treeSeedlingSystem.destroy()
this.villagerSystem.destroy()
}
}