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

View File

@@ -59,18 +59,41 @@ export class GameScene extends Phaser.Scene {
// Menu Inputs // Menu Inputs
this.input.keyboard?.on("keydown-I", () => { this.input.keyboard?.on("keydown-I", () => {
// Close minimap if it's open
if (this.dungeonRenderer.isMinimapVisible()) {
this.dungeonRenderer.toggleMinimap();
}
this.events.emit("toggle-menu"); this.events.emit("toggle-menu");
// Force update UI in case it opened // Force update UI in case it opened
this.emitUIUpdate(); this.emitUIUpdate();
}); });
this.input.keyboard?.on("keydown-ESC", () => { this.input.keyboard?.on("keydown-ESC", () => {
this.events.emit("close-menu"); 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) => { this.input.on("pointerdown", (p: Phaser.Input.Pointer) => {
if (!this.awaitingPlayer) return; if (!this.awaitingPlayer) return;
if (this.isMenuOpen) return; if (this.isMenuOpen || this.dungeonRenderer.isMinimapVisible()) return;
const tx = Math.floor(p.worldX / TILE_SIZE); const tx = Math.floor(p.worldX / TILE_SIZE);
const ty = Math.floor(p.worldY / TILE_SIZE); const ty = Math.floor(p.worldY / TILE_SIZE);
@@ -99,7 +122,7 @@ export class GameScene extends Phaser.Scene {
update() { update() {
if (!this.awaitingPlayer) return; if (!this.awaitingPlayer) return;
if (this.isMenuOpen) return; if (this.isMenuOpen || this.dungeonRenderer.isMinimapVisible()) return;
// Auto-walk one step per turn // Auto-walk one step per turn
if (this.playerPath.length >= 2) { if (this.playerPath.length >= 2) {

View File

@@ -12,6 +12,7 @@ export default class GameUI extends Phaser.Scene {
private menuText!: Phaser.GameObjects.Text; private menuText!: Phaser.GameObjects.Text;
private menuBg!: Phaser.GameObjects.Rectangle; private menuBg!: Phaser.GameObjects.Rectangle;
private menuButton!: Phaser.GameObjects.Container; private menuButton!: Phaser.GameObjects.Container;
private mapButton!: Phaser.GameObjects.Container;
constructor() { constructor() {
super({ key: "GameUI" }); super({ key: "GameUI" });
@@ -49,10 +50,10 @@ export default class GameUI extends Phaser.Scene {
private createMenu() { private createMenu() {
const cam = this.cameras.main; const cam = this.cameras.main;
// Button (top-right)
const btnW = 90; const btnW = 90;
const btnH = 28; const btnH = 28;
// Menu Button
const btnBg = this.add.rectangle(0, 0, btnW, btnH, 0x000000, 0.6).setStrokeStyle(1, 0xffffff, 0.8); 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); 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); this.menuButton.setPosition(cam.width - btnW / 2 - 10, btnH / 2 + 10);
}; };
placeButton(); placeButton();
this.scale.on("resize", placeButton); // Use scale manager resize this.scale.on("resize", placeButton);
btnBg.setInteractive({ useHandCursor: true }).on("pointerdown", () => this.toggleMenu()); 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) // Panel (center)
const panelW = 340; const panelW = 340;
const panelH = 220; const panelH = 220;
@@ -98,6 +114,11 @@ export default class GameUI extends Phaser.Scene {
private toggleMenu() { private toggleMenu() {
this.setMenuOpen(!this.menuOpen); 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) { private setMenuOpen(open: boolean) {
@@ -109,6 +130,13 @@ export default class GameUI extends Phaser.Scene {
gameScene.events.emit("menu-toggled", open); 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) { private updateUI(world: World, playerId: EntityId, levelIndex: number) {
this.updateHud(world, playerId, levelIndex); this.updateHud(world, playerId, levelIndex);
if (this.menuOpen) { if (this.menuOpen) {