diff --git a/src/scenes/DungeonRenderer.ts b/src/scenes/DungeonRenderer.ts index 08e7008..a9bf8ad 100644 --- a/src/scenes/DungeonRenderer.ts +++ b/src/scenes/DungeonRenderer.ts @@ -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; - - // Position in bottom right corner (accounting for zoom) - const x = cam.width / cam.zoom - minimapWidth - 10; - const y = cam.height / cam.zoom - minimapHeight - 10; - - this.minimapContainer.setPosition(x, y); + // Center on screen like menu + this.minimapContainer.setPosition(cam.width / 2, cam.height / 2); + } + + toggleMinimap() { + this.minimapVisible = !this.minimapVisible; + this.minimapContainer.setVisible(this.minimapVisible); + } + + 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; - - // Background - this.minimapBg.fillStyle(0x000000, 0.7); - this.minimapBg.fillRect(0, 0, minimapWidth, minimapHeight); - - // Border - this.minimapBg.lineStyle(1, 0x666666, 1); - this.minimapBg.strokeRect(0, 0, minimapWidth, minimapHeight); + // Calculate scale to fit map within panel + const padding = 20; + const availableWidth = this.minimapPanelWidth - padding * 2; + const availableHeight = this.minimapPanelHeight - padding * 2; + + const scaleX = availableWidth / this.world.width; + const scaleY = availableHeight / this.world.height; + const tileSize = Math.floor(Math.min(scaleX, scaleY)); + + // 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 ); } } diff --git a/src/scenes/GameScene.ts b/src/scenes/GameScene.ts index e8b9bd1..2c659a5 100644 --- a/src/scenes/GameScene.ts +++ b/src/scenes/GameScene.ts @@ -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) { diff --git a/src/scenes/GameUI.ts b/src/scenes/GameUI.ts index 70e749f..4c09c8c 100644 --- a/src/scenes/GameUI.ts +++ b/src/scenes/GameUI.ts @@ -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) {