♻️ resource-based passability: FOREST/ROCK walkable without a resource (Issue #22)
Previously FOREST and ROCK tile types were always impassable, making 30 % of forest floor and 50 % of rocky terrain permanently blocked even with no object on them. - Remove FOREST + ROCK from IMPASSABLE in types.ts - Add RESOURCE_TERRAIN set (FOREST, ROCK) for tiles that need resource check - WorldSystem: add resourceTiles Set<number> as O(1) spatial index - initResourceTiles() builds index from state on create() - addResourceTile() / removeResourceTile() keep it in sync at runtime - isPassable() now: impassable tiles → false | RESOURCE_TERRAIN → check index | else → true - GameScene: call addResourceTile() when SPAWN_RESOURCE fires (seedling matures) - VillagerSystem: call removeResourceTile() after chop / mine completes Side effect: trees fully enclosed by other trees are now reachable once an adjacent tree is cleared; the hasAdjacentPassable() guard in pickJob still correctly skips resources with zero passable neighbours. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import Phaser from 'phaser'
|
||||
import { TILE_SIZE, WORLD_TILES } from '../config'
|
||||
import { TileType, IMPASSABLE } from '../types'
|
||||
import { TileType, IMPASSABLE, RESOURCE_TERRAIN } from '../types'
|
||||
import { stateManager } from '../StateManager'
|
||||
|
||||
const BIOME_COLORS: Record<number, string> = {
|
||||
@@ -18,6 +18,12 @@ const BIOME_COLORS: Record<number, string> = {
|
||||
export class WorldSystem {
|
||||
private scene: Phaser.Scene
|
||||
private map!: Phaser.Tilemaps.Tilemap
|
||||
/**
|
||||
* Spatial index: tile keys (tileY * WORLD_TILES + tileX) for every tile
|
||||
* that is currently occupied by a tree or rock resource.
|
||||
* Used by isPassable() to decide if a FOREST or ROCK terrain tile is blocked.
|
||||
*/
|
||||
private resourceTiles = new Set<number>()
|
||||
private tileset!: Phaser.Tilemaps.Tileset
|
||||
private bgImage!: Phaser.GameObjects.Image
|
||||
private builtLayer!: Phaser.Tilemaps.TilemapLayer
|
||||
@@ -85,6 +91,8 @@ export class WorldSystem {
|
||||
|
||||
// Camera bounds
|
||||
this.scene.cameras.main.setBounds(0, 0, WORLD_TILES * TILE_SIZE, WORLD_TILES * TILE_SIZE)
|
||||
|
||||
this.initResourceTiles()
|
||||
}
|
||||
|
||||
/** Returns the built-tile tilemap layer (floor, wall, soil). */
|
||||
@@ -111,6 +119,10 @@ export class WorldSystem {
|
||||
|
||||
/**
|
||||
* Returns whether the tile at the given coordinates can be walked on.
|
||||
* Water and wall tiles are always impassable.
|
||||
* Forest and rock terrain tiles are only impassable when a resource
|
||||
* (tree or rock) currently occupies them — empty forest floor and bare
|
||||
* rocky ground are walkable.
|
||||
* Out-of-bounds tiles are treated as impassable.
|
||||
* @param tileX - Tile column
|
||||
* @param tileY - Tile row
|
||||
@@ -119,7 +131,45 @@ export class WorldSystem {
|
||||
if (tileX < 0 || tileY < 0 || tileX >= WORLD_TILES || tileY >= WORLD_TILES) return false
|
||||
const state = stateManager.getState()
|
||||
const tile = state.world.tiles[tileY * WORLD_TILES + tileX]
|
||||
return !IMPASSABLE.has(tile)
|
||||
if (IMPASSABLE.has(tile)) return false
|
||||
if (RESOURCE_TERRAIN.has(tile)) {
|
||||
return !this.resourceTiles.has(tileY * WORLD_TILES + tileX)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the resource tile index from the current world state.
|
||||
* Called once in create() so that isPassable() has an O(1) lookup.
|
||||
*/
|
||||
private initResourceTiles(): void {
|
||||
this.resourceTiles.clear()
|
||||
const state = stateManager.getState()
|
||||
for (const res of Object.values(state.world.resources)) {
|
||||
this.resourceTiles.add(res.tileY * WORLD_TILES + res.tileX)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a newly placed resource so isPassable() treats the tile as blocked.
|
||||
* Call this whenever a resource is added at runtime (e.g. a seedling matures).
|
||||
* @param tileX - Resource tile column
|
||||
* @param tileY - Resource tile row
|
||||
*/
|
||||
addResourceTile(tileX: number, tileY: number): void {
|
||||
this.resourceTiles.add(tileY * WORLD_TILES + tileX)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a resource from the tile index so isPassable() treats the tile as free.
|
||||
* Call this when a resource is removed at runtime (e.g. after chopping/mining).
|
||||
* Not strictly required when the tile type also changes (FOREST → DARK_GRASS),
|
||||
* but keeps the index clean for correctness.
|
||||
* @param tileX - Resource tile column
|
||||
* @param tileY - Resource tile row
|
||||
*/
|
||||
removeResourceTile(tileX: number, tileY: number): void {
|
||||
this.resourceTiles.delete(tileY * WORLD_TILES + tileX)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user