Make minimap toggleable
This commit is contained in:
@@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user