Add placeholder backpack and inventory UI

This commit is contained in:
Peter Stockings
2026-01-04 20:02:11 +11:00
parent 2ca51945fc
commit f67f488764
4 changed files with 229 additions and 7 deletions

View File

@@ -16,6 +16,13 @@ export default class GameUI extends Phaser.Scene {
private menuBg!: Phaser.GameObjects.Rectangle;
private menuButton!: Phaser.GameObjects.Container;
private mapButton!: Phaser.GameObjects.Container;
private backpackButton!: Phaser.GameObjects.Container;
// Inventory/Equipment Overlay
private inventoryOpen = false;
private invContainer!: Phaser.GameObjects.Container;
private equipmentSlots: Map<string, Phaser.GameObjects.Container> = new Map();
private backpackSlots: Phaser.GameObjects.Container[] = [];
// Death Screen
private deathContainer!: Phaser.GameObjects.Container;
@@ -29,6 +36,7 @@ export default class GameUI extends Phaser.Scene {
create() {
this.createHud();
this.createMenu();
this.createInventoryOverlay();
this.createDeathScreen();
// Listen for updates from GameScene
@@ -38,7 +46,11 @@ export default class GameUI extends Phaser.Scene {
});
gameScene.events.on("toggle-menu", () => this.toggleMenu());
gameScene.events.on("close-menu", () => this.setMenuOpen(false));
gameScene.events.on("toggle-inventory", () => this.toggleInventory());
gameScene.events.on("close-menu", () => {
this.setMenuOpen(false);
this.setInventoryOpen(false);
});
}
private createHud() {
@@ -114,9 +126,145 @@ export default class GameUI extends Phaser.Scene {
placePanel();
this.scale.on("resize", placePanel);
// Backpack Button (Bottom Left)
const bpBtnBg = this.add.rectangle(0, 0, btnW, btnH, 0x000000, 0.6).setStrokeStyle(1, 0xffffff, 0.8);
const bpBtnLabel = this.add.text(0, 0, "Backpack", { fontSize: "14px", color: "#ffffff" }).setOrigin(0.5);
this.backpackButton = this.add.container(0, 0, [bpBtnBg, bpBtnLabel]);
this.backpackButton.setDepth(1000);
const placeBpButton = () => {
this.backpackButton.setPosition(btnW / 2 + 10, cam.height - btnH / 2 - 10);
};
placeBpButton();
this.scale.on("resize", placeBpButton);
bpBtnBg.setInteractive({ useHandCursor: true }).on("pointerdown", () => this.toggleInventory());
this.setMenuOpen(false);
}
private createInventoryOverlay() {
const cam = this.cameras.main;
const panelW = 850;
const panelH = 550;
// Premium Background with Gradient
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.9);
bg.fillRect(-panelW / 2, -panelH / 2, panelW, panelH);
// Make the area interactive to capture clicks
const hitArea = new Phaser.Geom.Rectangle(-panelW / 2, -panelH / 2, panelW, panelH);
this.add.zone(0, 0, panelW, panelH).setInteractive(hitArea, Phaser.Geom.Rectangle.Contains);
bg.lineStyle(3, 0x443322, 1);
bg.strokeRect(-panelW / 2, -panelH / 2, panelW, panelH);
// Subtle inner border
bg.lineStyle(1, 0x887766, 0.3);
bg.strokeRect(-panelW / 2 + 5, -panelH / 2 + 5, panelW - 10, panelH - 10);
const title = this.add.text(0, -panelH / 2 + 25, "INVENTORY", {
fontSize: "28px",
color: "#d4af37",
fontStyle: "bold",
shadow: { blur: 2, color: "#000000", fill: true, offsetY: 2 }
}).setOrigin(0.5);
this.invContainer = this.add.container(0, 0, [bg, title]);
this.invContainer.setDepth(1001);
// --- Equipment Section (PoE Style) ---
const eqX = -200;
const eqY = 10;
const createSlot = (x: number, y: number, w: number, h: number, label: string, key: string) => {
const g = this.add.graphics();
// Outer border
g.lineStyle(2, 0x444444, 1);
g.strokeRect(-w / 2, -h / 2, w, h);
// Inner gradient-like background
g.fillStyle(0x1a1a1a, 1);
g.fillRect(-w / 2 + 1, -h / 2 + 1, w - 2, h - 2);
// Bottom highlight
g.lineStyle(1, 0x333333, 1);
g.lineBetween(-w / 2 + 2, h / 2 - 2, w / 2 - 2, h / 2 - 2);
const txt = this.add.text(0, 0, label, { fontSize: "11px", color: "#666666", fontStyle: "bold" }).setOrigin(0.5);
const container = this.add.container(x, y, [g, txt]);
this.equipmentSlots.set(key, container);
this.invContainer.add(container);
return container;
};
// Sizes based on PoE proportions
const sSmall = 54;
const sMed = 70;
const sLargeW = 90;
const sLargeH = 160;
// Central Column
createSlot(eqX, eqY - 140, sMed, sMed, "Head", "helmet"); // Helmet
createSlot(eqX, eqY - 20, sLargeW, 130, "Body", "bodyArmour"); // Body Armour
createSlot(eqX, eqY + 80, 100, 36, "Belt", "belt"); // Belt
// Sides (Large)
createSlot(eqX - 140, eqY - 50, sLargeW, sLargeH, "Main Hand", "mainHand"); // Main Hand
createSlot(eqX + 140, eqY - 50, sLargeW, sLargeH, "Off Hand", "offHand"); // Off Hand
// Inner Column Left (Ring)
createSlot(eqX - 80, eqY - 30, sSmall, sSmall, "Ring", "ringLeft");
// Inner Column Right (Ring)
createSlot(eqX + 80, eqY - 30, sSmall, sSmall, "Ring", "ringRight");
// Bottom Corners
createSlot(eqX - 100, eqY + 70, sMed, sMed, "Hands", "gloves");
createSlot(eqX + 100, eqY + 70, sMed, sMed, "Boots", "boots");
// --- Backpack Section (Right Side) ---
const bpX = 120;
const bpY = -panelH / 2 + 100;
const rows = 10;
const cols = 6;
const bpSlotSize = 42;
const bpTitle = this.add.text(bpX + (cols * (bpSlotSize + 4)) / 2 - 20, bpY - 40, "BACKPACK", {
fontSize: "18px",
color: "#d4af37",
fontStyle: "bold"
}).setOrigin(0.5);
this.invContainer.add(bpTitle);
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const x = bpX + c * (bpSlotSize + 4);
const y = bpY + r * (bpSlotSize + 4);
const g = this.add.graphics();
g.lineStyle(1, 0x333333, 1);
g.strokeRect(-bpSlotSize / 2, -bpSlotSize / 2, bpSlotSize, bpSlotSize);
g.fillStyle(0x0c0c0c, 1);
g.fillRect(-bpSlotSize / 2 + 0.5, -bpSlotSize / 2 + 0.5, bpSlotSize - 1, bpSlotSize - 1);
const container = this.add.container(x, y, [g]);
this.invContainer.add(container);
this.backpackSlots.push(container);
}
}
const placeInv = () => {
this.invContainer.setPosition(cam.width / 2, cam.height / 2);
};
placeInv();
this.scale.on("resize", placeInv);
this.setInventoryOpen(false);
}
private createDeathScreen() {
const cam = this.cameras.main;
const panelW = GAME_CONFIG.ui.menuPanelWidth + 40;
@@ -210,17 +358,46 @@ export default class GameUI extends Phaser.Scene {
}
private toggleMap() {
// Close menu and toggle minimap
// Close all and toggle minimap
this.setMenuOpen(false);
this.setInventoryOpen(false);
const gameScene = this.scene.get("GameScene");
gameScene.events.emit("toggle-minimap");
}
private toggleInventory() {
this.setInventoryOpen(!this.inventoryOpen);
if (this.inventoryOpen) {
this.setMenuOpen(false);
const gameScene = this.scene.get("GameScene");
gameScene.events.emit("request-ui-update");
}
}
private setInventoryOpen(open: boolean) {
this.inventoryOpen = open;
this.invContainer.setVisible(open);
const gameScene = this.scene.get("GameScene");
gameScene.events.emit("inventory-toggled", open);
}
private updateUI(world: World, playerId: EntityId, floorIndex: number) {
this.updateHud(world, playerId, floorIndex);
if (this.menuOpen) {
this.updateMenuText(world, playerId, floorIndex);
}
if (this.inventoryOpen) {
this.updateInventoryUI(world, playerId);
}
}
private updateInventoryUI(world: World, playerId: EntityId) {
const p = world.actors.get(playerId);
if (!p) return;
// Clear existing item icons/text from slots if needed (future refinement)
// For now we just show names or placeholders
}
private updateHud(world: World, playerId: EntityId, floorIndex: number) {