Compare commits
2 Commits
8d2c58cb5f
...
d9ef57c6b0
| Author | SHA1 | Date | |
|---|---|---|---|
| d9ef57c6b0 | |||
| 87f69b4774 |
@@ -59,6 +59,18 @@ export class UIScene extends Phaser.Scene {
|
|||||||
/** True while the zone-edit tool is active (shown in ESC priority stack). */
|
/** True while the zone-edit tool is active (shown in ESC priority stack). */
|
||||||
private inForesterZoneEdit = false
|
private inForesterZoneEdit = false
|
||||||
|
|
||||||
|
// ── Action Bar ────────────────────────────────────────────────────────────
|
||||||
|
private static readonly BAR_H = 48
|
||||||
|
private static readonly TRAY_H = 68
|
||||||
|
private actionBarBg!: Phaser.GameObjects.Rectangle
|
||||||
|
private actionBuildBtn!: Phaser.GameObjects.Rectangle
|
||||||
|
private actionBuildLabel!: Phaser.GameObjects.Text
|
||||||
|
private actionNisseBtn!: Phaser.GameObjects.Rectangle
|
||||||
|
private actionNisseLabel!: Phaser.GameObjects.Text
|
||||||
|
private actionTrayGroup!: Phaser.GameObjects.Group
|
||||||
|
private actionTrayVisible = false
|
||||||
|
private activeCategory: 'build' | 'nisse' | null = null
|
||||||
|
|
||||||
constructor() { super({ key: 'UI' }) }
|
constructor() { super({ key: 'UI' }) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +86,7 @@ export class UIScene extends Phaser.Scene {
|
|||||||
this.createBuildModeIndicator()
|
this.createBuildModeIndicator()
|
||||||
this.createFarmToolIndicator()
|
this.createFarmToolIndicator()
|
||||||
this.createDebugPanel()
|
this.createDebugPanel()
|
||||||
|
this.createActionBar()
|
||||||
|
|
||||||
const gameScene = this.scene.get('Game')
|
const gameScene = this.scene.get('Game')
|
||||||
gameScene.events.on('buildModeChanged', (a: boolean, b: BuildingType) => this.onBuildModeChanged(a, b))
|
gameScene.events.on('buildModeChanged', (a: boolean, b: BuildingType) => this.onBuildModeChanged(a, b))
|
||||||
@@ -99,6 +112,7 @@ export class UIScene extends Phaser.Scene {
|
|||||||
this.nisseInfoGroup = this.add.group()
|
this.nisseInfoGroup = this.add.group()
|
||||||
this.settingsGroup = this.add.group()
|
this.settingsGroup = this.add.group()
|
||||||
this.foresterPanelGroup = this.add.group()
|
this.foresterPanelGroup = this.add.group()
|
||||||
|
this.actionTrayGroup = this.add.group()
|
||||||
|
|
||||||
gameScene.events.on('foresterHutClicked', (id: string) => this.openForesterPanel(id))
|
gameScene.events.on('foresterHutClicked', (id: string) => this.openForesterPanel(id))
|
||||||
gameScene.events.on('foresterZoneEditEnded', () => this.onForesterEditEnded())
|
gameScene.events.on('foresterZoneEditEnded', () => this.onForesterEditEnded())
|
||||||
@@ -171,7 +185,7 @@ export class UIScene extends Phaser.Scene {
|
|||||||
|
|
||||||
/** Creates the centered hint text element near the bottom of the screen. */
|
/** Creates the centered hint text element near the bottom of the screen. */
|
||||||
private createHintText(): void {
|
private createHintText(): void {
|
||||||
this.hintText = this.add.text(this.scale.width / 2, this.scale.height - 40, '', {
|
this.hintText = this.add.text(this.scale.width / 2, this.scale.height - UIScene.BAR_H - 24, '', {
|
||||||
fontSize: '14px', color: '#ffff88', fontFamily: 'monospace',
|
fontSize: '14px', color: '#ffff88', fontFamily: 'monospace',
|
||||||
backgroundColor: '#00000099', padding: { x: 10, y: 5 },
|
backgroundColor: '#00000099', padding: { x: 10, y: 5 },
|
||||||
}).setOrigin(0.5).setScrollFactor(0).setDepth(100).setVisible(false)
|
}).setOrigin(0.5).setScrollFactor(0).setDepth(100).setVisible(false)
|
||||||
@@ -266,6 +280,10 @@ export class UIScene extends Phaser.Scene {
|
|||||||
this.villagerPanelVisible = false
|
this.villagerPanelVisible = false
|
||||||
this.villagerPanelGroup?.destroy(true)
|
this.villagerPanelGroup?.destroy(true)
|
||||||
this.scene.get('Game').events.emit('uiMenuClose')
|
this.scene.get('Game').events.emit('uiMenuClose')
|
||||||
|
if (this.activeCategory === 'nisse') {
|
||||||
|
this.activeCategory = null
|
||||||
|
this.updateCategoryHighlights()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,6 +543,7 @@ export class UIScene extends Phaser.Scene {
|
|||||||
if (this.foresterPanelVisible) { this.closeForesterPanel(); return }
|
if (this.foresterPanelVisible) { this.closeForesterPanel(); return }
|
||||||
if (this.contextMenuVisible) { this.hideContextMenu(); return }
|
if (this.contextMenuVisible) { this.hideContextMenu(); return }
|
||||||
if (this.buildMenuVisible) { this.closeBuildMenu(); return }
|
if (this.buildMenuVisible) { this.closeBuildMenu(); return }
|
||||||
|
if (this.actionTrayVisible) { this.closeActionTray(); return }
|
||||||
if (this.villagerPanelVisible) { this.closeVillagerPanel(); return }
|
if (this.villagerPanelVisible) { this.closeVillagerPanel(); return }
|
||||||
if (this.nisseInfoVisible) { this.closeNisseInfoPanel(); return }
|
if (this.nisseInfoVisible) { this.closeNisseInfoPanel(); return }
|
||||||
if (this.settingsVisible) { this.closeSettings(); return }
|
if (this.settingsVisible) { this.closeSettings(); return }
|
||||||
@@ -1137,6 +1156,152 @@ export class UIScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Action Bar ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the persistent bottom action bar with Build and Nisse category buttons.
|
||||||
|
* The bar is always visible; individual button highlights change with the active category.
|
||||||
|
*/
|
||||||
|
private createActionBar(): void {
|
||||||
|
const { width, height } = this.scale
|
||||||
|
const barY = height - UIScene.BAR_H
|
||||||
|
|
||||||
|
this.actionBarBg = this.add.rectangle(0, barY, width, UIScene.BAR_H, 0x080808, 0.92)
|
||||||
|
.setOrigin(0, 0).setScrollFactor(0).setDepth(300)
|
||||||
|
|
||||||
|
this.actionBuildBtn = this.add.rectangle(8, barY + 8, 88, 32, 0x1a3a1a, 0.9)
|
||||||
|
.setOrigin(0, 0).setScrollFactor(0).setDepth(301).setInteractive()
|
||||||
|
this.actionBuildBtn.on('pointerover', () => {
|
||||||
|
if (this.activeCategory !== 'build') this.actionBuildBtn.setFillStyle(0x2a5a2a, 0.9)
|
||||||
|
})
|
||||||
|
this.actionBuildBtn.on('pointerout', () => {
|
||||||
|
if (this.activeCategory !== 'build') this.actionBuildBtn.setFillStyle(0x1a3a1a, 0.9)
|
||||||
|
})
|
||||||
|
this.actionBuildBtn.on('pointerdown', () => this.toggleCategory('build'))
|
||||||
|
|
||||||
|
this.actionBuildLabel = this.add.text(52, barY + UIScene.BAR_H / 2, '🔨 Build', {
|
||||||
|
fontSize: '12px', color: '#cccccc', fontFamily: 'monospace',
|
||||||
|
}).setOrigin(0.5, 0.5).setScrollFactor(0).setDepth(302)
|
||||||
|
|
||||||
|
this.actionNisseBtn = this.add.rectangle(104, barY + 8, 88, 32, 0x1a1a3a, 0.9)
|
||||||
|
.setOrigin(0, 0).setScrollFactor(0).setDepth(301).setInteractive()
|
||||||
|
this.actionNisseBtn.on('pointerover', () => {
|
||||||
|
if (this.activeCategory !== 'nisse') this.actionNisseBtn.setFillStyle(0x2a2a5a, 0.9)
|
||||||
|
})
|
||||||
|
this.actionNisseBtn.on('pointerout', () => {
|
||||||
|
if (this.activeCategory !== 'nisse') this.actionNisseBtn.setFillStyle(0x1a1a3a, 0.9)
|
||||||
|
})
|
||||||
|
this.actionNisseBtn.on('pointerdown', () => this.toggleCategory('nisse'))
|
||||||
|
|
||||||
|
this.actionNisseLabel = this.add.text(148, barY + UIScene.BAR_H / 2, '👥 Nisse', {
|
||||||
|
fontSize: '12px', color: '#cccccc', fontFamily: 'monospace',
|
||||||
|
}).setOrigin(0.5, 0.5).setScrollFactor(0).setDepth(302)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the given action bar category on or off.
|
||||||
|
* Selecting the active category deselects it; selecting a new one closes the previous.
|
||||||
|
* @param cat - The category to toggle ('build' or 'nisse')
|
||||||
|
*/
|
||||||
|
private toggleCategory(cat: 'build' | 'nisse'): void {
|
||||||
|
if (this.activeCategory === cat) {
|
||||||
|
this.deactivateCategory()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Close whatever was open before
|
||||||
|
if (this.activeCategory === 'build') this.closeActionTray()
|
||||||
|
if (this.activeCategory === 'nisse' && this.villagerPanelVisible) this.closeVillagerPanel()
|
||||||
|
|
||||||
|
this.activeCategory = cat
|
||||||
|
this.updateCategoryHighlights()
|
||||||
|
|
||||||
|
if (cat === 'build') {
|
||||||
|
this.openActionTray()
|
||||||
|
} else {
|
||||||
|
this.openVillagerPanel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivates the currently active category, closing its associated panel or tray.
|
||||||
|
*/
|
||||||
|
private deactivateCategory(): void {
|
||||||
|
if (this.activeCategory === 'build') this.closeActionTray()
|
||||||
|
if (this.activeCategory === 'nisse' && this.villagerPanelVisible) this.closeVillagerPanel()
|
||||||
|
this.activeCategory = null
|
||||||
|
this.updateCategoryHighlights()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the visual highlight of the Build and Nisse buttons
|
||||||
|
* to reflect the current active category.
|
||||||
|
*/
|
||||||
|
private updateCategoryHighlights(): void {
|
||||||
|
this.actionBuildBtn.setFillStyle(this.activeCategory === 'build' ? 0x3d7a3d : 0x1a3a1a, 0.9)
|
||||||
|
this.actionNisseBtn.setFillStyle(this.activeCategory === 'nisse' ? 0x3d3d7a : 0x1a1a3a, 0.9)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and shows the building tool tray above the action bar.
|
||||||
|
* Each building is shown as a clickable tile with emoji and name.
|
||||||
|
*/
|
||||||
|
private openActionTray(): void {
|
||||||
|
if (this.actionTrayVisible) return
|
||||||
|
this.actionTrayVisible = true
|
||||||
|
this.actionTrayGroup.destroy(true)
|
||||||
|
this.actionTrayGroup = this.add.group()
|
||||||
|
|
||||||
|
const { width, height } = this.scale
|
||||||
|
const trayY = height - UIScene.BAR_H - UIScene.TRAY_H
|
||||||
|
|
||||||
|
const bg = this.add.rectangle(0, trayY, width, UIScene.TRAY_H, 0x0d0d0d, 0.88)
|
||||||
|
.setOrigin(0, 0).setScrollFactor(0).setDepth(300)
|
||||||
|
this.actionTrayGroup.add(bg)
|
||||||
|
|
||||||
|
const buildings: { kind: BuildingType; emoji: string; label: string }[] = [
|
||||||
|
{ kind: 'floor', emoji: '🪵', label: 'Floor' },
|
||||||
|
{ kind: 'wall', emoji: '🧱', label: 'Wall' },
|
||||||
|
{ kind: 'chest', emoji: '📦', label: 'Chest' },
|
||||||
|
{ kind: 'bed', emoji: '🛏', label: 'Bed' },
|
||||||
|
{ kind: 'stockpile_zone', emoji: '📦', label: 'Stockpile' },
|
||||||
|
{ kind: 'forester_hut', emoji: '🌲', label: 'Forester' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const itemW = 84
|
||||||
|
buildings.forEach((b, i) => {
|
||||||
|
const bx = 8 + i * (itemW + 4)
|
||||||
|
const btn = this.add.rectangle(bx, trayY + 4, itemW, UIScene.TRAY_H - 8, 0x1a2a1a, 0.9)
|
||||||
|
.setOrigin(0, 0).setScrollFactor(0).setDepth(301).setInteractive()
|
||||||
|
btn.on('pointerover', () => btn.setFillStyle(0x2d4a2d, 0.9))
|
||||||
|
btn.on('pointerout', () => btn.setFillStyle(0x1a2a1a, 0.9))
|
||||||
|
btn.on('pointerdown', () => {
|
||||||
|
this.closeActionTray()
|
||||||
|
this.deactivateCategory()
|
||||||
|
this.scene.get('Game').events.emit('selectBuilding', b.kind)
|
||||||
|
})
|
||||||
|
this.actionTrayGroup.add(btn)
|
||||||
|
this.actionTrayGroup.add(
|
||||||
|
this.add.text(bx + itemW / 2, trayY + 18, b.emoji, { fontSize: '18px' })
|
||||||
|
.setOrigin(0.5, 0.5).setScrollFactor(0).setDepth(302)
|
||||||
|
)
|
||||||
|
this.actionTrayGroup.add(
|
||||||
|
this.add.text(bx + itemW / 2, trayY + 44, b.label, {
|
||||||
|
fontSize: '10px', color: '#cccccc', fontFamily: 'monospace',
|
||||||
|
}).setOrigin(0.5, 0.5).setScrollFactor(0).setDepth(302)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides and destroys the building tool tray.
|
||||||
|
*/
|
||||||
|
private closeActionTray(): void {
|
||||||
|
if (!this.actionTrayVisible) return
|
||||||
|
this.actionTrayVisible = false
|
||||||
|
this.actionTrayGroup.destroy(true)
|
||||||
|
this.actionTrayGroup = this.add.group()
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Resize ───────────────────────────────────────────────────────────────
|
// ─── Resize ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1157,8 +1322,16 @@ export class UIScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bottom elements
|
// Bottom elements
|
||||||
this.hintText.setPosition(width / 2, height - 40)
|
this.hintText.setPosition(width / 2, height - UIScene.BAR_H - 24)
|
||||||
this.toastText.setPosition(width / 2, 60)
|
this.toastText.setPosition(width / 2, 60)
|
||||||
|
|
||||||
|
// Action bar — reposition persistent elements
|
||||||
|
this.actionBarBg.setPosition(0, height - UIScene.BAR_H).setSize(width, UIScene.BAR_H)
|
||||||
|
this.actionBuildBtn.setPosition(8, height - UIScene.BAR_H + 8)
|
||||||
|
this.actionBuildLabel.setPosition(48, height - UIScene.BAR_H + UIScene.BAR_H / 2)
|
||||||
|
this.actionNisseBtn.setPosition(104, height - UIScene.BAR_H + 8)
|
||||||
|
this.actionNisseLabel.setPosition(144, height - UIScene.BAR_H + UIScene.BAR_H / 2)
|
||||||
|
if (this.actionTrayVisible) this.closeActionTray()
|
||||||
// Close centered panels — their position is calculated on open, so they
|
// Close centered panels — their position is calculated on open, so they
|
||||||
// would be off-center if left open during a resize
|
// would be off-center if left open during a resize
|
||||||
if (this.buildMenuVisible) this.closeBuildMenu()
|
if (this.buildMenuVisible) this.closeBuildMenu()
|
||||||
|
|||||||
Reference in New Issue
Block a user