🐛 Fix resize, Nisse idle bug + rename Villager → Nisse #4

Merged
tekki merged 4 commits from feature/resize-fix into master 2026-03-20 18:58:42 +00:00
4 changed files with 41 additions and 12 deletions
Showing only changes of commit 8ed67313a8 - Show all commits

View File

@@ -7,6 +7,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Fixed
- 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
### Added ### 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 Folks actions in the game world
- Initial project setup: Phaser 3 + TypeScript + Vite - Initial project setup: Phaser 3 + TypeScript + Vite

View File

@@ -21,7 +21,9 @@ export class UIScene extends Phaser.Scene {
private buildModeText!: Phaser.GameObjects.Text private buildModeText!: Phaser.GameObjects.Text
private farmToolText!: Phaser.GameObjects.Text private farmToolText!: Phaser.GameObjects.Text
private coordsText!: Phaser.GameObjects.Text private coordsText!: Phaser.GameObjects.Text
private controlsHintText!: Phaser.GameObjects.Text
private popText!: Phaser.GameObjects.Text private popText!: Phaser.GameObjects.Text
private stockpileTitleText!: Phaser.GameObjects.Text
private contextMenuGroup!: Phaser.GameObjects.Group private contextMenuGroup!: Phaser.GameObjects.Group
private contextMenuVisible = false private contextMenuVisible = false
private inBuildMode = false private inBuildMode = false
@@ -80,7 +82,7 @@ export class UIScene extends Phaser.Scene {
private createStockpilePanel(): void { private createStockpilePanel(): void {
const x = this.scale.width - 178, y = 10 const x = this.scale.width - 178, y = 10
this.stockpilePanel = this.add.rectangle(x, y, 168, 165, 0x000000, 0.72).setOrigin(0, 0).setScrollFactor(0).setDepth(100) this.stockpilePanel = this.add.rectangle(x, y, 168, 165, 0x000000, 0.72).setOrigin(0, 0).setScrollFactor(0).setDepth(100)
this.add.text(x + 10, y + 7, '⚡ STOCKPILE', { fontSize: '11px', color: '#aaaaaa', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(101) this.stockpileTitleText = this.add.text(x + 10, y + 7, '⚡ STOCKPILE', { fontSize: '11px', color: '#aaaaaa', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(101)
const items = ['wood','stone','wheat_seed','carrot_seed','wheat','carrot'] as const const items = ['wood','stone','wheat_seed','carrot_seed','wheat','carrot'] as const
items.forEach((item, i) => { items.forEach((item, i) => {
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) 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)
@@ -286,7 +288,7 @@ export class UIScene extends Phaser.Scene {
private createCoordsDisplay(): void { private createCoordsDisplay(): void {
this.coordsText = this.add.text(10, this.scale.height - 24, '', { fontSize: '11px', color: '#666666', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(100) this.coordsText = this.add.text(10, this.scale.height - 24, '', { fontSize: '11px', color: '#666666', fontFamily: 'monospace' }).setScrollFactor(0).setDepth(100)
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] Villagers', {
fontSize: '10px', color: '#444444', fontFamily: 'monospace', backgroundColor: '#00000066', padding: { x: 4, y: 2 } fontSize: '10px', color: '#444444', fontFamily: 'monospace', backgroundColor: '#00000066', padding: { x: 4, y: 2 }
}).setScrollFactor(0).setDepth(100) }).setScrollFactor(0).setDepth(100)
} }
@@ -359,10 +361,33 @@ export class UIScene extends Phaser.Scene {
// ─── Resize ─────────────────────────────────────────────────────────────── // ─── Resize ───────────────────────────────────────────────────────────────
/**
* Repositions all fixed UI elements after a canvas resize.
* Open overlay panels are closed so they reopen correctly centered.
*/
private repositionUI(): void { private repositionUI(): void {
const { width, height } = this.scale const { width, height } = this.scale
this.hintText.setPosition(width/2, height - 40)
this.toastText.setPosition(width/2, 60) // Stockpile panel — anchored to top-right; move all elements by the delta
const newPanelX = width - 178
const deltaX = newPanelX - this.stockpilePanel.x
if (deltaX !== 0) {
this.stockpilePanel.setX(newPanelX)
this.stockpileTitleText.setX(this.stockpileTitleText.x + deltaX)
this.stockpileTexts.forEach(t => t.setX(t.x + deltaX))
this.popText.setX(this.popText.x + deltaX)
}
// Bottom elements
this.hintText.setPosition(width / 2, height - 40)
this.toastText.setPosition(width / 2, 60)
this.coordsText.setPosition(10, height - 24) this.coordsText.setPosition(10, height - 24)
this.controlsHintText.setPosition(10, height - 42)
// Close centered panels — their position is calculated on open, so they
// would be off-center if left open during a resize
if (this.buildMenuVisible) this.closeBuildMenu()
if (this.villagerPanelVisible) this.closeVillagerPanel()
if (this.contextMenuVisible) this.hideContextMenu()
} }
} }

View File

@@ -93,8 +93,8 @@ export class BuildingSystem {
// Update ghost to follow mouse (snapped to tile grid) // Update ghost to follow mouse (snapped to tile grid)
const ptr = this.scene.input.activePointer const ptr = this.scene.input.activePointer
const worldX = this.scene.cameras.main.scrollX + ptr.x const worldX = ptr.worldX
const worldY = this.scene.cameras.main.scrollY + ptr.y const worldY = ptr.worldY
const tileX = Math.floor(worldX / TILE_SIZE) const tileX = Math.floor(worldX / TILE_SIZE)
const tileY = Math.floor(worldY / TILE_SIZE) const tileY = Math.floor(worldY / TILE_SIZE)
const snapX = tileX * TILE_SIZE + TILE_SIZE / 2 const snapX = tileX * TILE_SIZE + TILE_SIZE / 2
@@ -142,8 +142,8 @@ export class BuildingSystem {
} }
private tryPlace(ptr: Phaser.Input.Pointer): void { private tryPlace(ptr: Phaser.Input.Pointer): void {
const worldX = this.scene.cameras.main.scrollX + ptr.x const worldX = ptr.worldX
const worldY = this.scene.cameras.main.scrollY + ptr.y const worldY = ptr.worldY
const tileX = Math.floor(worldX / TILE_SIZE) const tileX = Math.floor(worldX / TILE_SIZE)
const tileY = Math.floor(worldY / TILE_SIZE) const tileY = Math.floor(worldY / TILE_SIZE)

View File

@@ -80,9 +80,8 @@ export class FarmingSystem {
// ─── Tool actions ───────────────────────────────────────────────────────── // ─── Tool actions ─────────────────────────────────────────────────────────
private useToolAt(ptr: Phaser.Input.Pointer): void { private useToolAt(ptr: Phaser.Input.Pointer): void {
const cam = this.scene.cameras.main const worldX = ptr.worldX
const worldX = cam.scrollX + ptr.x const worldY = ptr.worldY
const worldY = cam.scrollY + ptr.y
const tileX = Math.floor(worldX / TILE_SIZE) const tileX = Math.floor(worldX / TILE_SIZE)
const tileY = Math.floor(worldY / TILE_SIZE) const tileY = Math.floor(worldY / TILE_SIZE)
const state = stateManager.getState() const state = stateManager.getState()