diff --git a/CHANGELOG.md b/CHANGELOG.md index b831616..b59d2f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,18 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Fixed +- Nisse no longer get stuck idle after depositing items at the stockpile +- Working Nisse now reset to idle on game load (like walking ones), preventing stale AI state +- Stale jobs with empty carry are now cleared after work completes, avoiding a false "haul to stockpile" loop - UI elements (stockpile panel, controls hint) now reposition correctly after window resize - Centered overlay panels (build menu, villager panel) close on resize so they reopen at the correct position - Mouse world coordinates now use `ptr.worldX`/`ptr.worldY` in BuildingSystem and FarmingSystem, fixing misalignment after resize or zoom +### Changed +- Villagers are now called **Nisse** throughout the UI (panel, controls hint, stockpile display, context menu, spawn message) + ### Added -- Right-click context menu: suppresses browser default, shows Build and Folks actions in the game world +- Right-click context menu: suppresses browser default, shows Build and Nisse actions in the game world - Initial project setup: Phaser 3 + TypeScript + Vite - Core scenes: `BootScene`, `GameScene`, `UIScene` - Systems: `BuildingSystem`, `CameraSystem`, `FarmingSystem`, `PlayerSystem`, diff --git a/src/StateManager.ts b/src/StateManager.ts index 35211c3..f1fc7e6 100644 --- a/src/StateManager.ts +++ b/src/StateManager.ts @@ -176,9 +176,9 @@ class StateManager { if (!p.world.crops) p.world.crops = {} if (!p.world.villagers) p.world.villagers = {} if (!p.world.stockpile) p.world.stockpile = {} - // Reset walking villagers to idle on load + // Reset in-flight AI states to idle on load so runtime timers start fresh for (const v of Object.values(p.world.villagers)) { - if (v.aiState === 'walking') v.aiState = 'idle' + if (v.aiState === 'walking' || v.aiState === 'working') v.aiState = 'idle' } return p } catch (_) { return null } diff --git a/src/scenes/UIScene.ts b/src/scenes/UIScene.ts index 509dd9f..fa94dd0 100644 --- a/src/scenes/UIScene.ts +++ b/src/scenes/UIScene.ts @@ -88,7 +88,7 @@ export class UIScene extends Phaser.Scene { const t = this.add.text(x + 10, y + 26 + i * 22, `${ITEM_ICONS[item]} ${item}: 0`, { fontSize: '13px', color: '#88dd88', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(101) this.stockpileTexts.set(item, t) }) - this.popText = this.add.text(x + 10, y + 145, '👥 Pop: 0 / 0', { fontSize: '11px', color: '#aaaaaa', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(101) + this.popText = this.add.text(x + 10, y + 145, '👥 Nisse: 0 / 0', { fontSize: '11px', color: '#aaaaaa', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(101) } private updateStockpile(): void { @@ -104,7 +104,7 @@ export class UIScene extends Phaser.Scene { const state = stateManager.getState() const beds = Object.values(state.world.buildings).filter(b => b.kind === 'bed').length const current = Object.keys(state.world.villagers).length - this.popText?.setText(`👥 Pop: ${current} / ${beds} [V] manage`) + this.popText?.setText(`👥 Nisse: ${current} / ${beds} [V]`) } // ─── Hint ───────────────────────────────────────────────────────────────── @@ -204,13 +204,13 @@ export class UIScene extends Phaser.Scene { this.villagerPanelGroup.add(bg) this.villagerPanelGroup.add( - this.add.text(px + panelW/2, py + 12, '👥 VILLAGERS [V] close', { fontSize: '12px', color: '#aaaaaa', fontFamily: 'monospace' }) + this.add.text(px + panelW/2, py + 12, '👥 NISSE [V] close', { fontSize: '12px', color: '#aaaaaa', fontFamily: 'monospace' }) .setOrigin(0.5, 0).setScrollFactor(0).setDepth(211) ) if (villagers.length === 0) { this.villagerPanelGroup.add( - this.add.text(px + panelW/2, py + panelH/2, 'No villagers yet.\nBuild a 🛏 Bed first!', { + this.add.text(px + panelW/2, py + panelH/2, 'No Nisse yet.\nBuild a 🛏 Bed first!', { fontSize: '13px', color: '#666666', fontFamily: 'monospace', align: 'center' }).setOrigin(0.5).setScrollFactor(0).setDepth(211) ) @@ -288,7 +288,7 @@ export class UIScene extends Phaser.Scene { private createCoordsDisplay(): void { this.coordsText = this.add.text(10, this.scale.height - 24, '', { fontSize: '11px', color: '#666666', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(100) - this.controlsHintText = this.add.text(10, this.scale.height - 42, '[WASD] Pan [Scroll] Zoom [F] Farm [B] Build [V] Villagers', { + this.controlsHintText = this.add.text(10, this.scale.height - 42, '[WASD] Pan [Scroll] Zoom [F] Farm [B] Build [V] Nisse', { fontSize: '10px', color: '#444444', fontFamily: 'monospace', backgroundColor: '#00000066', padding: { x: 4, y: 2 } }).setScrollFactor(0).setDepth(100) } @@ -324,7 +324,7 @@ export class UIScene extends Phaser.Scene { action: () => { this.hideContextMenu(); this.scene.get('Game').events.emit('uiRequestBuildMenu') }, }, { - label: '👥 Folks', + label: '👥 Nisse', action: () => { this.hideContextMenu(); this.toggleVillagerPanel() }, }, ] diff --git a/src/systems/VillagerSystem.ts b/src/systems/VillagerSystem.ts index 4c3ebfd..f6eb302 100644 --- a/src/systems/VillagerSystem.ts +++ b/src/systems/VillagerSystem.ts @@ -170,6 +170,7 @@ export class VillagerSystem { case 'stockpile': this.adapter.send({ type: 'VILLAGER_DEPOSIT', villagerId: v.id }) this.adapter.send({ type: 'VILLAGER_SET_AI', villagerId: v.id, aiState: 'idle' }) + rt.idleScanTimer = 0 // scan for a new job immediately after deposit break case 'bed': @@ -217,7 +218,13 @@ export class VillagerSystem { } } - // Back to idle so decideAction handles depositing + // If the harvest produced nothing (resource already gone), clear the stale job + // so tickIdle does not try to walk to a stockpile with nothing to deposit. + if (!v.job?.carrying || !Object.values(v.job.carrying).some(n => (n ?? 0) > 0)) { + this.adapter.send({ type: 'VILLAGER_SET_JOB', villagerId: v.id, job: null }) + } + + // Back to idle — tickIdle will handle hauling to stockpile if carrying items this.adapter.send({ type: 'VILLAGER_SET_AI', villagerId: v.id, aiState: 'idle' }) } @@ -342,7 +349,7 @@ export class VillagerSystem { this.adapter.send({ type: 'SPAWN_VILLAGER', villager }) this.spawnSprite(villager) - this.onMessage?.(`${name} has joined the settlement! 🏘`) + this.onMessage?.(`${name} the Nisse has arrived! 🏘`) } // ─── Sprite management ────────────────────────────────────────────────────