Fixes#34. Alle Object.values()-Aufrufe werden einmal am Anfang von
pickJob() extrahiert und in allen Branches wiederverwendet. Der
Forester-Loop rief zuvor fuer jedes Zone-Tile 4x Object.values() auf.
JOB_ICONS als Modul-Konstante, Math.min-spread durch Schleife ersetzt.
Expands isOccluded() from same-column-only to a 3x4 tile window
(tileX+-1, tileY+1..4) to catch trees whose canopy extends sideways
and well above the trunk tile. Outline scale bumped to 1.15.
Silhouette now hidden by default and toggled on per frame only when
isOccluded() detects a tree, rock or building 1–3 tiles below the Nisse.
Adds WorldSystem.hasResourceAt() for O(1) tile lookup. Outline colour
changed to light blue (0xaaddff) at scale 1.1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#33. Each Nisse now has a white filled outline sprite at depth 900
that is always visible above trees and buildings. The main sprite uses
Y-based depth (floor(y/TILE_SIZE)+5) so Nisse sort correctly with world
objects. Name label, energy bar and job icon moved to depth 901/902 so
they remain readable regardless of occlusion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
CHANGE_TILE only called worldSystem.setTile() (built-tile layer only),
never refreshTerrainTile() — so chopped trees stayed visually dark-green
(FOREST color) even though the tile type was already DARK_GRASS.
- adapter.onAction for CHANGE_TILE now also calls refreshTerrainTile()
→ all tile transitions (chop, mine, seedling maturation) update the
canvas pixel immediately and consistently in one place
- Remove now-redundant explicit refreshTerrainTile() call in
TreeSeedlingSystem (the adapter handler covers it)
- Tile-recovery path in GameScene (stateManager.tickTileRecovery) is
NOT routed through the adapter, so its manual refreshTerrainTile()
call is kept as-is
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Trees/rocks fully enclosed by impassable tiles have no passable neighbour
for A* to jump from — pathfinding always returns null, causing a tight
1.5 s retry loop that fills the work log with identical entries.
- Add hasAdjacentPassable() helper: checks all 8 neighbours of a tile
- pickJob now skips chop/mine candidates with no passable neighbour
- idleScanTimer on pathfind failure raised 1500 → 4000 ms as safety net
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Clicking a Nisse opens a top-left panel showing name, AI status,
energy bar, active job, job priority buttons, and a live work log
(last 10 of 20 runtime-only entries). Closes via ESC, ✕, or clicking
another Nisse. Dynamic parts (status/energy/job/log) refresh each
frame without rebuilding the full group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
Replaces plain cam.setZoom() with zoom-to-mouse: after each zoom step
the scroll is corrected by (mouseOffset from center) * (1/zBefore - 1/zAfter),
keeping the world point under the cursor fixed. Also fixes getCenterWorld()
which previously divided by zoom incorrectly. Added JSDoc to all methods.
- Scroll wheel now zooms toward the mouse cursor instead of screen center
- Middle mouse button held: pan camera by dragging
- Both actions respect current zoom level