Forester Radius Preview beim Platzieren (Issue #47) #56

Open
claude wants to merge 1 commits from feature/forester-radius-preview into master
2 changed files with 43 additions and 2 deletions
Showing only changes of commit a8d745408a - Show all commits

View File

@@ -8,6 +8,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Added ### Added
- **Forester Radius Preview** (Issue #47): When placing a Forester Hut, a semi-transparent green overlay shows the full planting zone radius (Chebyshev 5 = 11×11 tiles) following the ghost cursor; disappears on cancel
- **Demolish Mode** (Issue #50): New 💥 Demolish button in the action bar; hover shows a red ghost over any building with a refund percentage; buildings demolished within 3 minutes return 100% of costs (linear decay to 0%); mine footprint tiles are unblocked on teardown; Nisse working inside a demolished building are rescued and resume idle; tile types are restored where applicable (floor/wall/chest → grass) - **Demolish Mode** (Issue #50): New 💥 Demolish button in the action bar; hover shows a red ghost over any building with a refund percentage; buildings demolished within 3 minutes return 100% of costs (linear decay to 0%); mine footprint tiles are unblocked on teardown; Nisse working inside a demolished building are rescued and resume idle; tile types are restored where applicable (floor/wall/chest → grass)
- **Mine Building** (Issue #42): 3×2 building placeable only on resource-free ROCK tiles (costs: 200 wood + 50 stone); Nisse with mine priority walk to the entrance, disappear inside for 15 s, then reappear carrying 2 stone; up to 3 Nisse work simultaneously; ⛏ X/3 status label shown directly on the building in world space; surface rock harvesting remains functional alongside the building - **Mine Building** (Issue #42): 3×2 building placeable only on resource-free ROCK tiles (costs: 200 wood + 50 stone); Nisse with mine priority walk to the entrance, disappear inside for 15 s, then reappear carrying 2 stone; up to 3 Nisse work simultaneously; ⛏ X/3 status label shown directly on the building in world space; surface rock harvesting remains functional alongside the building

View File

@@ -1,5 +1,5 @@
import Phaser from 'phaser' import Phaser from 'phaser'
import { TILE_SIZE, BUILDING_COSTS, DEMOLISH_REFUND_MS } from '../config' import { TILE_SIZE, BUILDING_COSTS, DEMOLISH_REFUND_MS, FORESTER_ZONE_RADIUS } from '../config'
import { TileType, IMPASSABLE } from '../types' import { TileType, IMPASSABLE } from '../types'
import type { BuildingType, BuildingState, ItemId } from '../types' import type { BuildingType, BuildingState, ItemId } from '../types'
import { stateManager } from '../StateManager' import { stateManager } from '../StateManager'
@@ -27,6 +27,7 @@ export class BuildingSystem {
private selectedBuilding: BuildingType = 'floor' private selectedBuilding: BuildingType = 'floor'
private ghost!: Phaser.GameObjects.Rectangle private ghost!: Phaser.GameObjects.Rectangle
private ghostLabel!: Phaser.GameObjects.Text private ghostLabel!: Phaser.GameObjects.Text
private foresterRadiusOverlay!: Phaser.GameObjects.Graphics
private buildKey!: Phaser.Input.Keyboard.Key private buildKey!: Phaser.Input.Keyboard.Key
private cancelKey!: Phaser.Input.Keyboard.Key private cancelKey!: Phaser.Input.Keyboard.Key
@@ -63,6 +64,10 @@ export class BuildingSystem {
this.ghostLabel.setVisible(false) this.ghostLabel.setVisible(false)
this.ghostLabel.setOrigin(0.5, 1) this.ghostLabel.setOrigin(0.5, 1)
this.foresterRadiusOverlay = this.scene.add.graphics()
this.foresterRadiusOverlay.setDepth(999)
this.foresterRadiusOverlay.setVisible(false)
this.buildKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.B) this.buildKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.B)
this.cancelKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.ESC) this.cancelKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.ESC)
@@ -138,6 +143,7 @@ export class BuildingSystem {
this.active = false this.active = false
this.ghost.setVisible(false) this.ghost.setVisible(false)
this.ghostLabel.setVisible(false) this.ghostLabel.setVisible(false)
this.foresterRadiusOverlay.setVisible(false)
this.onModeChange?.(false, this.selectedBuilding) this.onModeChange?.(false, this.selectedBuilding)
} }
@@ -193,6 +199,7 @@ export class BuildingSystem {
/** /**
* Updates the green/red build-mode ghost to follow the mouse, snapped to the tile grid. * Updates the green/red build-mode ghost to follow the mouse, snapped to the tile grid.
* For forester_hut, also draws a semi-transparent radius preview overlay.
*/ */
private updateBuildGhost(): void { private updateBuildGhost(): void {
const ptr = this.scene.input.activePointer const ptr = this.scene.input.activePointer
@@ -213,6 +220,38 @@ export class BuildingSystem {
const costs = BUILDING_COSTS[this.selectedBuilding] ?? {} const costs = BUILDING_COSTS[this.selectedBuilding] ?? {}
const costStr = Object.entries(costs).map(([k, v]) => `${v}${k[0].toUpperCase()}`).join(' ') const costStr = Object.entries(costs).map(([k, v]) => `${v}${k[0].toUpperCase()}`).join(' ')
this.ghostLabel.setText(`${this.selectedBuilding} [${costStr}]`) this.ghostLabel.setText(`${this.selectedBuilding} [${costStr}]`)
if (this.selectedBuilding === 'forester_hut') {
this.updateForesterRadiusOverlay(tileX, tileY)
} else {
this.foresterRadiusOverlay.setVisible(false)
}
}
/**
* Redraws the forester radius preview overlay centered on the given tile.
* Shows the Chebyshev-radius planting zone as a semi-transparent green fill
* with a dotted border.
* @param centerTileX - Tile column of the hut
* @param centerTileY - Tile row of the hut
*/
private updateForesterRadiusOverlay(centerTileX: number, centerTileY: number): void {
const r = FORESTER_ZONE_RADIUS
const px = (centerTileX - r) * TILE_SIZE
const py = (centerTileY - r) * TILE_SIZE
const pw = (r * 2 + 1) * TILE_SIZE
const ph = (r * 2 + 1) * TILE_SIZE
this.foresterRadiusOverlay.clear()
this.foresterRadiusOverlay.setVisible(true)
// Semi-transparent green fill
this.foresterRadiusOverlay.fillStyle(0x44ff44, 0.08)
this.foresterRadiusOverlay.fillRect(px, py, pw, ph)
// Solid border
this.foresterRadiusOverlay.lineStyle(2, 0x44ff44, 0.5)
this.foresterRadiusOverlay.strokeRect(px, py, pw, ph)
} }
/** /**
@@ -421,10 +460,11 @@ export class BuildingSystem {
} }
/** /**
* Cleans up ghost sprites on scene shutdown. * Cleans up ghost sprites and overlays on scene shutdown.
*/ */
destroy(): void { destroy(): void {
this.ghost.destroy() this.ghost.destroy()
this.ghostLabel.destroy() this.ghostLabel.destroy()
this.foresterRadiusOverlay.destroy()
} }
} }