Make minimap toggleable

This commit is contained in:
Peter Stockings
2026-01-04 10:14:01 +11:00
parent 0f28a2212e
commit 6e3763a17b
3 changed files with 114 additions and 45 deletions

View File

@@ -20,9 +20,10 @@ export class DungeonRenderer {
// Minimap
private minimapGfx!: Phaser.GameObjects.Graphics;
private minimapContainer!: Phaser.GameObjects.Container;
private minimapBg!: Phaser.GameObjects.Graphics;
private minimapScale = 2; // pixels per tile
private minimapPadding = 8;
private minimapBg!: Phaser.GameObjects.Rectangle;
private minimapPanelWidth = 340; // Match menu size
private minimapPanelHeight = 220; // Match menu size
private minimapVisible = false; // Off by default
constructor(scene: Phaser.Scene) {
this.scene = scene;
@@ -35,13 +36,24 @@ export class DungeonRenderer {
private initMinimap() {
this.minimapContainer = this.scene.add.container(0, 0);
this.minimapContainer.setScrollFactor(0); // Fixed to camera
this.minimapContainer.setDepth(100);
this.minimapContainer.setDepth(1001); // Same as menu
// Background panel (like menu)
this.minimapBg = this.scene.add
.rectangle(0, 0, this.minimapPanelWidth, this.minimapPanelHeight, 0x000000, 0.8)
.setStrokeStyle(1, 0xffffff, 0.9)
.setInteractive(); // Capture clicks
this.minimapBg = this.scene.add.graphics();
this.minimapGfx = this.scene.add.graphics();
this.minimapContainer.add(this.minimapBg);
this.minimapContainer.add(this.minimapGfx);
// Position in center
this.positionMinimap();
// Start hidden
this.minimapContainer.setVisible(false);
}
initializeLevel(world: World) {
@@ -61,14 +73,17 @@ export class DungeonRenderer {
private positionMinimap() {
const cam = this.scene.cameras.main;
const minimapWidth = this.world.width * this.minimapScale + this.minimapPadding * 2;
const minimapHeight = this.world.height * this.minimapScale + this.minimapPadding * 2;
// Center on screen like menu
this.minimapContainer.setPosition(cam.width / 2, cam.height / 2);
}
// Position in bottom right corner (accounting for zoom)
const x = cam.width / cam.zoom - minimapWidth - 10;
const y = cam.height / cam.zoom - minimapHeight - 10;
toggleMinimap() {
this.minimapVisible = !this.minimapVisible;
this.minimapContainer.setVisible(this.minimapVisible);
}
this.minimapContainer.setPosition(x, y);
isMinimapVisible(): boolean {
return this.minimapVisible;
}
computeFov(playerId: EntityId) {
@@ -178,21 +193,24 @@ export class DungeonRenderer {
}
private renderMinimap() {
this.minimapBg.clear();
this.minimapGfx.clear();
if (!this.world) return;
const minimapWidth = this.world.width * this.minimapScale + this.minimapPadding * 2;
const minimapHeight = this.world.height * this.minimapScale + this.minimapPadding * 2;
// Calculate scale to fit map within panel
const padding = 20;
const availableWidth = this.minimapPanelWidth - padding * 2;
const availableHeight = this.minimapPanelHeight - padding * 2;
// Background
this.minimapBg.fillStyle(0x000000, 0.7);
this.minimapBg.fillRect(0, 0, minimapWidth, minimapHeight);
const scaleX = availableWidth / this.world.width;
const scaleY = availableHeight / this.world.height;
const tileSize = Math.floor(Math.min(scaleX, scaleY));
// Border
this.minimapBg.lineStyle(1, 0x666666, 1);
this.minimapBg.strokeRect(0, 0, minimapWidth, minimapHeight);
// Center the map within the panel
const mapPixelWidth = this.world.width * tileSize;
const mapPixelHeight = this.world.height * tileSize;
const offsetX = -mapPixelWidth / 2;
const offsetY = -mapPixelHeight / 2;
// Draw only seen tiles
for (let y = 0; y < this.world.height; y++) {
@@ -207,10 +225,10 @@ export class DungeonRenderer {
this.minimapGfx.fillStyle(color, 1);
this.minimapGfx.fillRect(
this.minimapPadding + x * this.minimapScale,
this.minimapPadding + y * this.minimapScale,
this.minimapScale,
this.minimapScale
offsetX + x * tileSize,
offsetY + y * tileSize,
tileSize,
tileSize
);
}
}
@@ -222,10 +240,10 @@ export class DungeonRenderer {
if (this.seen[exitIdx] === 1) {
this.minimapGfx.fillStyle(0xffd166, 1);
this.minimapGfx.fillRect(
this.minimapPadding + ex * this.minimapScale,
this.minimapPadding + ey * this.minimapScale,
this.minimapScale,
this.minimapScale
offsetX + ex * tileSize,
offsetY + ey * tileSize,
tileSize,
tileSize
);
}
@@ -234,10 +252,10 @@ export class DungeonRenderer {
if (player) {
this.minimapGfx.fillStyle(0x66ff66, 1);
this.minimapGfx.fillRect(
this.minimapPadding + player.pos.x * this.minimapScale,
this.minimapPadding + player.pos.y * this.minimapScale,
this.minimapScale,
this.minimapScale
offsetX + player.pos.x * tileSize,
offsetY + player.pos.y * tileSize,
tileSize,
tileSize
);
}
@@ -250,10 +268,10 @@ export class DungeonRenderer {
this.minimapGfx.fillStyle(0xff6666, 1);
this.minimapGfx.fillRect(
this.minimapPadding + a.pos.x * this.minimapScale,
this.minimapPadding + a.pos.y * this.minimapScale,
this.minimapScale,
this.minimapScale
offsetX + a.pos.x * tileSize,
offsetY + a.pos.y * tileSize,
tileSize,
tileSize
);
}
}

View File

@@ -59,18 +59,41 @@ export class GameScene extends Phaser.Scene {
// Menu Inputs
this.input.keyboard?.on("keydown-I", () => {
// Close minimap if it's open
if (this.dungeonRenderer.isMinimapVisible()) {
this.dungeonRenderer.toggleMinimap();
}
this.events.emit("toggle-menu");
// Force update UI in case it opened
this.emitUIUpdate();
});
this.input.keyboard?.on("keydown-ESC", () => {
this.events.emit("close-menu");
// Also close minimap
if (this.dungeonRenderer.isMinimapVisible()) {
this.dungeonRenderer.toggleMinimap();
}
});
this.input.keyboard?.on("keydown-M", () => {
// Close menu if it's open
this.events.emit("close-menu");
this.dungeonRenderer.toggleMinimap();
});
// Mouse click -> compute path (only during player turn, and not while menu is open)
// Listen for Map button click from UI
this.events.on("toggle-minimap", () => {
this.dungeonRenderer.toggleMinimap();
});
// Listen for UI update requests
this.events.on("request-ui-update", () => {
this.emitUIUpdate();
});
// Mouse click -> compute path (only during player turn, and not while menu/minimap is open)
this.input.on("pointerdown", (p: Phaser.Input.Pointer) => {
if (!this.awaitingPlayer) return;
if (this.isMenuOpen) return;
if (this.isMenuOpen || this.dungeonRenderer.isMinimapVisible()) return;
const tx = Math.floor(p.worldX / TILE_SIZE);
const ty = Math.floor(p.worldY / TILE_SIZE);
@@ -99,7 +122,7 @@ export class GameScene extends Phaser.Scene {
update() {
if (!this.awaitingPlayer) return;
if (this.isMenuOpen) return;
if (this.isMenuOpen || this.dungeonRenderer.isMinimapVisible()) return;
// Auto-walk one step per turn
if (this.playerPath.length >= 2) {

View File

@@ -12,6 +12,7 @@ export default class GameUI extends Phaser.Scene {
private menuText!: Phaser.GameObjects.Text;
private menuBg!: Phaser.GameObjects.Rectangle;
private menuButton!: Phaser.GameObjects.Container;
private mapButton!: Phaser.GameObjects.Container;
constructor() {
super({ key: "GameUI" });
@@ -49,10 +50,10 @@ export default class GameUI extends Phaser.Scene {
private createMenu() {
const cam = this.cameras.main;
// Button (top-right)
const btnW = 90;
const btnH = 28;
// Menu Button
const btnBg = this.add.rectangle(0, 0, btnW, btnH, 0x000000, 0.6).setStrokeStyle(1, 0xffffff, 0.8);
const btnLabel = this.add.text(0, 0, "Menu", { fontSize: "14px", color: "#ffffff" }).setOrigin(0.5);
@@ -63,10 +64,25 @@ export default class GameUI extends Phaser.Scene {
this.menuButton.setPosition(cam.width - btnW / 2 - 10, btnH / 2 + 10);
};
placeButton();
this.scale.on("resize", placeButton); // Use scale manager resize
this.scale.on("resize", placeButton);
btnBg.setInteractive({ useHandCursor: true }).on("pointerdown", () => this.toggleMenu());
// Map Button (left of Menu button)
const mapBtnBg = this.add.rectangle(0, 0, btnW, btnH, 0x000000, 0.6).setStrokeStyle(1, 0xffffff, 0.8);
const mapBtnLabel = this.add.text(0, 0, "Map", { fontSize: "14px", color: "#ffffff" }).setOrigin(0.5);
this.mapButton = this.add.container(0, 0, [mapBtnBg, mapBtnLabel]);
this.mapButton.setDepth(1000);
const placeMapButton = () => {
this.mapButton.setPosition(cam.width - btnW / 2 - 10 - btnW - 5, btnH / 2 + 10);
};
placeMapButton();
this.scale.on("resize", placeMapButton);
mapBtnBg.setInteractive({ useHandCursor: true }).on("pointerdown", () => this.toggleMap());
// Panel (center)
const panelW = 340;
const panelH = 220;
@@ -98,6 +114,11 @@ export default class GameUI extends Phaser.Scene {
private toggleMenu() {
this.setMenuOpen(!this.menuOpen);
// Request UI update when menu is opened to populate the text
if (this.menuOpen) {
const gameScene = this.scene.get("GameScene");
gameScene.events.emit("request-ui-update");
}
}
private setMenuOpen(open: boolean) {
@@ -109,6 +130,13 @@ export default class GameUI extends Phaser.Scene {
gameScene.events.emit("menu-toggled", open);
}
private toggleMap() {
// Close menu and toggle minimap
this.setMenuOpen(false);
const gameScene = this.scene.get("GameScene");
gameScene.events.emit("toggle-minimap");
}
private updateUI(world: World, playerId: EntityId, levelIndex: number) {
this.updateHud(world, playerId, levelIndex);
if (this.menuOpen) {