Files
novafarma/src/scenes/UIScene.js
2025-12-08 14:01:41 +01:00

1449 lines
52 KiB
JavaScript

class UIScene extends Phaser.Scene {
constructor() {
super({ key: 'UIScene' });
}
create() {
console.log('🖥️ UIScene: Initialized!');
// Pridobi reference na GameScene podatke (ko bodo na voljo)
this.gameScene = this.scene.get('GameScene');
// Setup UI Container
// Shared Overlay (DayNight + Weather)
// Rendered here to be screen-space (HUD) and ignore zoom
this.overlayGraphics = this.add.graphics();
this.overlayGraphics.setDepth(-1000); // Behind UI elements
this.drawUI();
this.createInventoryBar();
this.createGoldDisplay();
this.createVirtualJoystick();
this.createClock();
// this.createDebugInfo();
this.createSettingsButton();
// Resize event
this.scale.on('resize', this.resize, this);
// Crafting Menu (C)
this.input.keyboard.on('keydown-C', () => {
this.toggleCraftingMenu();
});
// Save (F5)
this.input.keyboard.on('keydown-F5', () => {
if (this.gameScene) this.gameScene.saveGame();
});
// Factory Reset (F8) - Fix za "zginla drevesa"
this.input.keyboard.on('keydown-F8', () => {
console.log('🔥 FACTORY RESET - Clearing Save & Reloading...');
localStorage.removeItem('novafarma_savefile');
window.location.reload();
});
// Collection (J)
this.input.keyboard.on('keydown-J', () => {
this.toggleCollectionMenu();
});
// Skill Tree (K)
this.input.keyboard.on('keydown-K', () => {
this.toggleSkillTree();
});
// Define Recipes
this.craftingRecipes = [
{ id: 'axe', name: 'Stone Axe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for chopping trees.' },
{ id: 'pickaxe', name: 'Stone Pickaxe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for mining rocks.' },
{ id: 'hoe', name: 'Stone Hoe', req: { 'wood': 2, 'stone': 2 }, output: 1, type: 'tool', desc: 'Prepares soil for planting.' },
{ id: 'sword', name: 'Stone Sword', req: { 'wood': 5, 'stone': 2 }, output: 1, type: 'weapon', desc: 'Deals damage to zombies.' },
{ id: 'fence', name: 'Wood Fence', req: { 'wood': 2 }, output: 1, type: 'building', desc: 'Simple barrier.' },
{ id: 'chest', name: 'Wooden Chest', req: { 'wood': 20 }, output: 1, type: 'furniture', desc: 'Stores items.' },
{ id: 'furnace', name: 'Furnace', req: { 'stone': 20 }, output: 1, type: 'machine', desc: 'Smelts ores into bars.' },
{ id: 'mint', name: 'Mint', req: { 'stone': 50, 'iron_bar': 5 }, output: 1, type: 'machine', desc: 'Mints coins from bars.' },
{ id: 'grave', name: 'Grave', req: { 'stone': 10 }, output: 1, type: 'furniture', desc: 'Resting place for zombies.' }
];
}
// ... (rest of class) ...
toggleCraftingMenu() {
if (!this.craftingContainer) this.createCraftingMenu();
this.craftingContainer.setVisible(!this.craftingContainer.visible);
// Pause/Resume game?
// if (this.gameScene) this.gameScene.physics.world.isPaused = this.craftingContainer.visible;
}
toggleCrafting() {
this.toggleCraftingMenu();
}
toggleInventory() {
// Za zdaj odpre crafting meni, ker nimamo ločenega "Big Inventory"
this.toggleCraftingMenu();
}
createCraftingMenu() {
const w = 600;
const h = 400;
const x = this.scale.width / 2;
const y = this.scale.height / 2;
this.craftingContainer = this.add.container(x, y);
this.craftingContainer.setDepth(2000); // Top of everything
// 1. Background (Main Window)
const bg = this.add.graphics();
bg.fillStyle(0x1a1a2e, 0.98); // Dark Blue theme
bg.fillRect(-w / 2, -h / 2, w, h);
bg.lineStyle(2, 0x4e4e6e, 1);
bg.strokeRect(-w / 2, -h / 2, w, h);
this.craftingContainer.add(bg);
// Header
const titleBg = this.add.rectangle(0, -h / 2 + 25, w, 50, 0x16213e);
this.craftingContainer.add(titleBg);
const title = this.add.text(0, -h / 2 + 25, 'WORKBENCH', {
fontSize: '24px', fontFamily: 'Courier New', fontStyle: 'bold', color: '#ffffff'
}).setOrigin(0.5);
this.craftingContainer.add(title);
// Close Button
const closeBtn = this.add.text(w / 2 - 20, -h / 2 + 25, 'X', {
fontSize: '24px', color: '#ff4444', fontStyle: 'bold'
}).setOrigin(0.5);
closeBtn.setInteractive({ useHandCursor: true })
.on('pointerdown', () => this.toggleCraftingMenu());
this.craftingContainer.add(closeBtn);
// 2. Layout Containers
// Left Panel (List)
this.recipeListContainer = this.add.container(-w / 2 + 20, -h / 2 + 70);
this.craftingContainer.add(this.recipeListContainer);
// Right Panel (Details)
this.detailsContainer = this.add.container(20, -h / 2 + 70);
this.craftingContainer.add(this.detailsContainer);
// Initial render
this.selectedRecipe = null;
this.refreshRecipeList();
this.craftingContainer.setVisible(false);
}
refreshRecipeList() {
this.recipeListContainer.removeAll(true);
// Filter recipes
const unlocked = this.craftingRecipes.filter(r => {
// Check Blueprint System
if (this.gameScene.blueprintSystem) {
return this.gameScene.blueprintSystem.isUnlocked(r.id);
}
return true; // Fallback
});
let y = 0;
unlocked.forEach((recipe, i) => {
const btnBg = this.add.rectangle(130, y + 20, 260, 36, 0x2a2a3e);
btnBg.setInteractive({ useHandCursor: true });
// Hover effect
btnBg.on('pointerover', () => btnBg.setFillStyle(0x3a3a5e));
btnBg.on('pointerout', () => {
if (this.selectedRecipe !== recipe) btnBg.setFillStyle(0x2a2a3e);
else btnBg.setFillStyle(0x4a4a6e);
});
// Click
btnBg.on('pointerdown', () => {
this.selectedRecipe = recipe;
this.refreshRecipeList(); // Redraw selection highlight
this.showRecipeDetails(recipe);
});
// Highlight selected
if (this.selectedRecipe === recipe) {
btnBg.setFillStyle(0x4a4a6e);
btnBg.setStrokeStyle(1, 0xffff00);
} else {
btnBg.setStrokeStyle(1, 0x4e4e6e);
}
const nameText = this.add.text(10, y + 10, recipe.name.toUpperCase(), {
fontSize: '14px', fontFamily: 'monospace', fill: '#ffffff'
});
this.recipeListContainer.add(btnBg);
this.recipeListContainer.add(nameText);
y += 40;
});
// Select first if none selected
if (!this.selectedRecipe && unlocked.length > 0) {
this.selectedRecipe = unlocked[0];
this.showRecipeDetails(unlocked[0]);
}
}
showRecipeDetails(recipe) {
this.detailsContainer.removeAll(true);
if (!recipe) return;
// Title
const title = this.add.text(0, 0, recipe.name, {
fontSize: '22px', fontStyle: 'bold', fill: '#FFD700', stroke: '#000', strokeThickness: 2
});
this.detailsContainer.add(title);
// Description
const desc = this.add.text(0, 35, recipe.desc, {
fontSize: '14px', fill: '#aaaaaa', wordWrap: { width: 250 }
});
this.detailsContainer.add(desc);
// Requirements Header
this.detailsContainer.add(this.add.text(0, 90, 'REQUIRED MATERIALS:', {
fontSize: '14px', fill: '#ffffff', fontStyle: 'bold'
}));
// Requirements List
let y = 120;
let canCraft = true;
const inv = this.gameScene.inventorySystem;
for (const [item, count] of Object.entries(recipe.req)) {
const has = inv ? inv.getItemCount(item) : 0;
const hasEnough = has >= count;
if (!hasEnough) canCraft = false;
const color = hasEnough ? '#55ff55' : '#ff5555';
const icon = hasEnough ? '✓' : '✗';
const reqText = this.add.text(0, y,
`${icon} ${count}x ${item.toUpperCase()} (Have: ${has})`,
{ fontSize: '14px', fill: color, fontFamily: 'monospace' }
);
this.detailsContainer.add(reqText);
y += 20;
}
// CRAFT BUTTON
const btnY = 300;
const btnBg = this.add.rectangle(130, btnY, 200, 50, canCraft ? 0x00aa00 : 0x550000);
if (canCraft) {
btnBg.setInteractive({ useHandCursor: true });
btnBg.on('pointerover', () => btnBg.setFillStyle(0x00cc00));
btnBg.on('pointerout', () => btnBg.setFillStyle(0x00aa00));
btnBg.on('pointerdown', () => this.tryCraft(recipe));
}
const btnText = this.add.text(130, btnY, 'CRAFT ITEM', {
fontSize: '20px', fill: canCraft ? '#ffffff' : '#888888', fontStyle: 'bold'
}).setOrigin(0.5);
this.detailsContainer.add(btnBg);
this.detailsContainer.add(btnText);
}
tryCraft(recipe) {
if (!this.gameScene || !this.gameScene.inventorySystem) return;
const inv = this.gameScene.inventorySystem;
// Double check cost
for (const [item, count] of Object.entries(recipe.req)) {
if (!inv.hasItem(item, count)) {
console.log(`Craft fail: Missing ${item}`);
return;
}
}
// Consume
for (const [item, count] of Object.entries(recipe.req)) {
inv.removeItem(item, count);
}
// Add Item
inv.addItem(recipe.id, recipe.output);
console.log(`Crafted ${recipe.name}!`);
// Sound & Visuals
if (this.gameScene.soundManager) this.gameScene.soundManager.playSuccess();
this.cameras.main.flash(200, 255, 255, 255);
// Refresh UI
this.refreshRecipeList();
this.showRecipeDetails(recipe);
}
resize(gameSize) {
this.width = gameSize.width;
this.height = gameSize.height;
this.cameras.main.setViewport(0, 0, this.width, this.height);
// Re-create UI elements at new positions
this.createClock();
this.createGoldDisplay();
this.createInventoryBar();
this.createInventoryBar();
this.createDebugInfo();
this.createSettingsButton();
// Refresh data
if (this.gameScene) {
if (this.gameScene.inventorySystem) {
this.updateInventory(this.gameScene.inventorySystem.slots);
}
// Clock/Gold update automatically in next frame
// Overlay fixes itself via Systems
if (this.overlayGraphics) {
this.overlayGraphics.clear();
}
}
}
drawUI() {
const x = 20;
const y = 20;
const width = 150; // Zmanjšana širina (300 -> 150)
const height = 15; // Zmanjšana višina (30 -> 15)
const padding = 10;
// 1. Health Bar
this.healthBar = this.drawBar(x, y, width, height, 0xff0000, 100, 'HP');
// 2. Hunger Bar
this.hungerBar = this.drawBar(x, y + height + padding, width, height, 0xff8800, 80, 'HUN');
// 3. Thirst Bar
this.thirstBar = this.drawBar(x, y + (height + padding) * 2, width, height, 0x0088ff, 90, 'H2O');
// 4. XP Bar
this.XPBar = this.drawBar(x, y + (height + padding) * 3, width, height, 0xFFD700, 0, 'XP');
// 5. Level Display
this.LevelDisplay = this.add.text(x, y + (height + padding) * 4, 'LV: 1', {
fontSize: '18px', fontFamily: 'Courier New', fill: '#FFD700', fontStyle: 'bold'
});
}
drawBar(x, y, width, height, color, initialPercent = 100, label = '') {
// Label
if (label) {
this.add.text(x, y - 12, label, { fontSize: '12px', fontFamily: 'Courier New', fill: '#ffffff' });
}
// Background
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.5);
bg.fillRect(x, y, width, height);
bg.lineStyle(2, 0xffffff, 0.2);
bg.strokeRect(x, y, width, height);
// Fill
const fill = this.add.graphics();
// Initial draw
fill.fillStyle(color, 1);
const maxWidth = width - 4;
const currentWidth = (maxWidth * initialPercent) / 100;
fill.fillRect(x + 2, y + 2, currentWidth, height - 4);
return { bg, fill, x, y, width, height, color };
}
setBarValue(bar, percent) {
// Clamp 0-100
percent = Phaser.Math.Clamp(percent, 0, 100);
bar.fill.clear();
bar.fill.fillStyle(bar.color, 1);
const maxWidth = bar.width - 4;
const currentWidth = (maxWidth * percent) / 100;
bar.fill.fillRect(bar.x + 2, bar.y + 2, currentWidth, bar.height - 4);
}
createInventoryBar() {
// Clean up
if (this.inventorySlots) {
this.inventorySlots.forEach(slot => {
if (slot.itemSprite) slot.itemSprite.destroy();
if (slot.itemText) slot.itemText.destroy();
if (slot.countText) slot.countText.destroy();
slot.destroy();
});
}
const slotCount = 9;
const slotSize = 48; // 48x48 sloti
const padding = 5;
const totalWidth = (slotCount * slotSize) + ((slotCount - 1) * padding);
const startX = (this.scale.width - totalWidth) / 2;
const startY = this.scale.height - slotSize - 20;
this.inventorySlots = [];
if (this.selectedSlot === undefined) this.selectedSlot = 0;
for (let i = 0; i < slotCount; i++) {
const x = startX + i * (slotSize + padding);
// Slot Background
const slot = this.add.graphics();
// Draw function to update style based on selection
slot.userData = { x, y: startY, size: slotSize, index: i };
this.drawSlot(slot, false);
// Add number text
this.add.text(x + 2, startY + 2, (i + 1).toString(), {
fontSize: '10px',
fontFamily: 'monospace',
fill: '#ffffff'
});
this.inventorySlots.push(slot);
}
// Select first one initially
this.selectSlot(0);
// Keyboard inputs 1-9
this.input.keyboard.on('keydown', (event) => {
const num = parseInt(event.key);
if (!isNaN(num) && num >= 1 && num <= 9) {
this.selectSlot(num - 1);
}
});
// Mouse scroll for inventory (optional)
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
if (deltaY > 0) {
this.selectSlot((this.selectedSlot + 1) % slotCount);
} else if (deltaY < 0) {
this.selectSlot((this.selectedSlot - 1 + slotCount) % slotCount);
}
});
}
drawSlot(graphics, isSelected) {
const { x, y, size } = graphics.userData;
graphics.clear();
// Background
graphics.fillStyle(0x000000, 0.6);
graphics.fillRect(x, y, size, size);
// Border
if (isSelected) {
graphics.lineStyle(3, 0xffff00, 1); // Yellow thick border for selection
} else {
graphics.lineStyle(2, 0x888888, 0.5); // Grey thin border
}
graphics.strokeRect(x, y, size, size);
}
selectSlot(index) {
// Deselect current
if (this.inventorySlots[this.selectedSlot]) {
this.drawSlot(this.inventorySlots[this.selectedSlot], false);
}
this.selectedSlot = index;
// Select new
this.drawSlot(this.inventorySlots[this.selectedSlot], true);
}
updateInventory(slots) {
if (!this.inventorySlots) return;
for (let i = 0; i < this.inventorySlots.length; i++) {
const slotGraphics = this.inventorySlots[i];
// Clean up old visual elements
if (slotGraphics.itemSprite) {
slotGraphics.itemSprite.destroy();
slotGraphics.itemSprite = null;
}
if (slotGraphics.itemText) {
slotGraphics.itemText.destroy();
slotGraphics.itemText = null;
}
if (slotGraphics.countText) {
slotGraphics.countText.destroy();
slotGraphics.countText = null;
}
if (slots[i]) {
const { x, y, size } = slotGraphics.userData;
const type = slots[i].type;
const textureKey = `item_${type}`; // e.g., item_axe, item_wood
// Check if texture exists
if (this.textures.exists(textureKey)) {
// Draw Sprite
const sprite = this.add.sprite(x + size / 2, y + size / 2, textureKey);
// Larger scale for better visibility (fill most of the slot)
const scale = (size - 4) / Math.max(sprite.width, sprite.height);
sprite.setScale(scale * 1.2); // 20% bigger!
// Crisp 2D rendering
sprite.setTexture(textureKey);
slotGraphics.itemSprite = sprite;
} else {
// Fallback Text - bigger and bolder
const text = this.add.text(x + size / 2, y + size / 2,
type.substring(0, 3).toUpperCase(),
{ fontSize: '16px', align: 'center', color: '#ffff00', stroke: '#000', strokeThickness: 3, fontStyle: 'bold' }
).setOrigin(0.5);
slotGraphics.itemText = text;
}
// Draw Count (if > 1) - bigger and bolder
if (slots[i].count > 1) {
const countText = this.add.text(x + size - 2, y + size - 2,
slots[i].count.toString(),
{ fontSize: '14px', align: 'right', color: '#ffffff', stroke: '#000', strokeThickness: 3, fontStyle: 'bold' }
).setOrigin(1, 1);
slotGraphics.countText = countText;
}
}
}
}
createClock() {
if (this.clockBg) this.clockBg.destroy();
if (this.clockText) this.clockText.destroy();
// Clock box top right
const x = this.scale.width - 150;
const y = 20;
// Background
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.5);
bg.fillRect(x, y, 130, 40);
bg.lineStyle(2, 0xffffff, 0.8);
bg.strokeRect(x, y, 130, 40);
this.clockBg = bg; // Save ref
this.clockText = this.add.text(x + 65, y + 20, 'Day 1 - 08:00', {
fontSize: '14px',
fontFamily: 'Courier New',
fill: '#ffffff',
fontStyle: 'bold'
});
this.clockText.setOrigin(0.5, 0.5);
}
createGoldDisplay() {
if (this.goldBg) this.goldBg.destroy();
if (this.goldText) this.goldText.destroy();
const x = this.scale.width - 150;
const y = 70; // Below clock
// Background
const bg = this.add.graphics();
bg.fillStyle(0xDAA520, 0.2); // Goldish bg
bg.fillRect(x, y, 130, 30);
bg.lineStyle(2, 0xFFD700, 0.8);
bg.strokeRect(x, y, 130, 30);
this.goldBg = bg; // Save ref
this.goldText = this.add.text(x + 65, y + 15, 'GOLD: 0', {
fontSize: '16px',
fontFamily: 'Courier New',
fill: '#FFD700', // Gold color
fontStyle: 'bold'
});
this.goldText.setOrigin(0.5, 0.5);
}
updateGold(amount) {
if (this.goldText) {
this.goldText.setText(`GOLD: ${amount}`);
}
}
createDebugInfo() {
if (this.debugText) this.debugText.destroy();
if (this.debugBg) this.debugBg.destroy();
const x = this.scale.width - 170;
const y = 120; // Below Gold and Clock area
// Background
this.debugBg = this.add.graphics();
this.debugBg.fillStyle(0x000000, 0.7);
this.debugBg.fillRect(x, y, 160, 70);
this.debugBg.setDepth(2999);
this.debugText = this.add.text(x + 10, y + 10, 'Waiting for stats...', {
fontSize: '12px',
fontFamily: 'monospace',
fill: '#ffffff',
stroke: '#000000',
strokeThickness: 2
});
this.debugText.setDepth(3000);
}
update() {
if (!this.gameScene) return;
// Sync HP Bar
if (this.gameScene.player && this.healthBar) {
const hp = this.gameScene.player.hp !== undefined ? this.gameScene.player.hp : 100;
const maxHp = this.gameScene.player.maxHp || 100;
this.setBarValue(this.healthBar, (hp / maxHp) * 100);
}
if (this.gameScene.statsSystem && this.hungerBar && this.thirstBar) {
const stats = this.gameScene.statsSystem;
this.setBarValue(this.hungerBar, stats.hunger);
this.setBarValue(this.thirstBar, stats.thirst);
}
}
toggleBuildMenu(isVisible) {
if (!this.buildMenuContainer) {
this.createBuildMenuInfo();
}
if (isVisible) {
this.buildMenuContainer.setVisible(true);
this.buildMenuContainer.y = -100; // Start off-screen
this.tweens.add({
targets: this.buildMenuContainer,
y: 100, // Target pos
duration: 300,
ease: 'Back.easeOut'
});
} else {
// Slide out (optional) or just hide
this.buildMenuContainer.setVisible(false);
}
}
createBuildMenuInfo() {
this.buildMenuContainer = this.add.container(this.width / 2, 100);
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.7);
bg.fillRect(-150, 0, 300, 100);
bg.lineStyle(2, 0x00FF00, 1);
bg.strokeRect(-150, 0, 300, 100);
this.buildMenuContainer.add(bg);
const title = this.add.text(0, 10, 'BUILD MODE [B]', { fontSize: '18px', fill: '#00FF00', fontStyle: 'bold' }).setOrigin(0.5, 0);
this.buildMenuContainer.add(title);
const info = this.add.text(0, 40,
'[1] Fence (2 Wood)\n[2] Wall (2 Stone)\n[3] House (20W 20S 50G)',
{ fontSize: '14px', fill: '#ffffff', align: 'center' }
).setOrigin(0.5, 0);
this.buildMenuContainer.add(info);
this.selectedBuildingText = this.add.text(0, 80, 'Selected: Fence', { fontSize: '14px', fill: '#FFFF00' }).setOrigin(0.5, 0);
this.buildMenuContainer.add(this.selectedBuildingText);
this.buildMenuContainer.setVisible(false);
}
updateBuildSelection(name) {
if (this.selectedBuildingText) {
this.selectedBuildingText.setText(`Selected: ${name.toUpperCase()}`);
}
}
showProjectMenu(ruinData, onContribute) {
if (!this.projectMenuContainer) {
this.createProjectMenu();
}
// Update info
const costText = `Req: ${ruinData.reqWood} Wood, ${ruinData.reqStone} Stone`;
this.projectInfoText.setText(`RESTORING RUINS\n${costText}`);
this.onContributeCallback = onContribute;
this.projectMenuContainer.setVisible(true);
this.projectMenuContainer.setDepth(10000);
}
createProjectMenu() {
this.projectMenuContainer = this.add.container(this.width / 2, this.height / 2);
// BG
const bg = this.add.graphics();
bg.fillStyle(0x222222, 0.9);
bg.fillRect(-150, -100, 300, 200);
bg.lineStyle(2, 0x00FFFF, 1);
bg.strokeRect(-150, -100, 300, 200);
this.projectMenuContainer.add(bg);
// Title
const title = this.add.text(0, -80, 'PROJECT: RESTORATION', { fontSize: '20px', fill: '#00FFFF', fontStyle: 'bold' }).setOrigin(0.5);
this.projectMenuContainer.add(title);
// Info
this.projectInfoText = this.add.text(0, -20, 'Req: ???', { fontSize: '16px', fill: '#ffffff', align: 'center' }).setOrigin(0.5);
this.projectMenuContainer.add(this.projectInfoText);
// Button
const btnBg = this.add.rectangle(0, 50, 200, 40, 0x00aa00);
btnBg.setInteractive();
btnBg.on('pointerdown', () => {
if (this.onContributeCallback) this.onContributeCallback();
// Close menu? Or keep open to see result?
// For now close
this.projectMenuContainer.setVisible(false);
});
this.projectMenuContainer.add(btnBg);
const btnText = this.add.text(0, 50, 'CONTRIBUTE', { fontSize: '18px', fill: '#ffffff' }).setOrigin(0.5);
this.projectMenuContainer.add(btnText);
// Close Button
const closeBtn = this.add.text(130, -90, 'X', { fontSize: '20px', fill: '#ff0000' }).setOrigin(0.5);
closeBtn.setInteractive();
closeBtn.on('pointerdown', () => this.projectMenuContainer.setVisible(false));
this.projectMenuContainer.add(closeBtn);
this.projectMenuContainer.setVisible(false);
}
showTradeMenu(inventorySystem) {
if (!this.tradeMenuContainer) {
this.createTradeMenu(inventorySystem);
}
this.updateTradeMenu(inventorySystem);
this.tradeMenuContainer.setVisible(true);
this.tradeMenuContainer.setDepth(10000);
}
createTradeMenu(inventorySystem) {
this.tradeMenuContainer = this.add.container(this.width / 2, this.height / 2);
// BG
const bg = this.add.graphics();
bg.fillStyle(0x222222, 0.95);
bg.fillRect(-200, -150, 400, 300);
bg.lineStyle(2, 0xFFD700, 1); // Gold border
bg.strokeRect(-200, -150, 400, 300);
this.tradeMenuContainer.add(bg);
// Title
const title = this.add.text(0, -130, 'MERCHANT SHOP', { fontSize: '24px', fill: '#FFD700', fontStyle: 'bold' }).setOrigin(0.5);
this.tradeMenuContainer.add(title);
// Close Button
const closeBtn = this.add.text(180, -140, 'X', { fontSize: '20px', fill: '#ff0000', fontStyle: 'bold' }).setOrigin(0.5);
closeBtn.setInteractive({ useHandCursor: true });
closeBtn.on('pointerdown', () => this.tradeMenuContainer.setVisible(false));
this.tradeMenuContainer.add(closeBtn);
// Content Container (for items)
this.tradeItemsContainer = this.add.container(0, 0);
this.tradeMenuContainer.add(this.tradeItemsContainer);
}
updateTradeMenu(inventorySystem) {
this.tradeItemsContainer.removeAll(true);
// Items to Sell (Player has -> Merchant wants)
// Hardcoded prices for now
const prices = {
'wheat': { price: 10, type: 'sell' },
'wood': { price: 2, type: 'sell' },
'seeds': { price: 5, type: 'buy' }
};
const startY = -80;
let index = 0;
// Header
const header = this.add.text(-180, startY, 'ITEM PRICE ACTION', { fontSize: '16px', fill: '#888888' });
this.tradeItemsContainer.add(header);
// 1. Sell Wheat
this.createTradeRow(inventorySystem, 'wheat', prices.wheat.price, 'SELL', index++, startY + 30);
// 2. Sell Wood
this.createTradeRow(inventorySystem, 'wood', prices.wood.price, 'SELL', index++, startY + 30);
// 3. Buy Seeds
this.createTradeRow(inventorySystem, 'seeds', prices.seeds.price, 'BUY', index++, startY + 30);
}
createTradeRow(inv, itemKey, price, action, index, yOffset) {
const y = yOffset + (index * 40);
// Name
const name = this.add.text(-180, y, itemKey.toUpperCase(), { fontSize: '18px', fill: '#ffffff' });
this.tradeItemsContainer.add(name);
// Price
const priceText = this.add.text(-50, y, `${price}g`, { fontSize: '18px', fill: '#FFD700' });
this.tradeItemsContainer.add(priceText);
// Button
const btnX = 100;
const btnBg = this.add.rectangle(btnX, y + 10, 80, 30, action === 'BUY' ? 0x008800 : 0x880000);
btnBg.setInteractive({ useHandCursor: true });
const btnLabel = this.add.text(btnX, y + 10, action, { fontSize: '16px', fill: '#ffffff' }).setOrigin(0.5);
btnBg.on('pointerdown', () => {
if (action === 'SELL') {
if (inv.hasItem(itemKey, 1)) {
inv.removeItem(itemKey, 1);
inv.gold += price;
inv.updateUI();
// Refresh visuals?
} else {
// Fail feedback
btnLabel.setText('NO ITEM');
this.scene.time.delayedCall(500, () => btnLabel.setText(action));
}
} else if (action === 'BUY') {
if (inv.gold >= price) {
inv.gold -= price;
inv.addItem(itemKey, 1);
inv.updateUI();
} else {
// Fail feedback
btnLabel.setText('NO GOLD');
this.scene.time.delayedCall(500, () => btnLabel.setText(action));
}
}
});
this.tradeItemsContainer.add(btnBg);
this.tradeItemsContainer.add(btnLabel);
}
// --- SETTINGS MENU ---
createSettingsButton() {
if (this.settingsBtn) this.settingsBtn.destroy();
this.settingsBtn = this.add.text(10, 10, '⚙️ SETTINGS', {
fontSize: '16px',
fill: '#ffffff',
backgroundColor: '#000000',
padding: { x: 5, y: 5 }
});
this.settingsBtn.setInteractive({ useHandCursor: true });
this.settingsBtn.on('pointerdown', () => this.toggleSettingsMenu());
}
toggleSettingsMenu() {
if (!this.settingsContainer) {
this.createSettingsMenu();
}
this.settingsContainer.setVisible(!this.settingsContainer.visible);
if (this.settingsContainer.visible) {
this.settingsContainer.setDepth(20000); // Always on top
}
}
createSettingsMenu() {
this.settingsContainer = this.add.container(this.width / 2, this.height / 2);
// BG
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.9);
bg.fillRect(-150, -120, 300, 240);
bg.lineStyle(2, 0x888888, 1);
bg.strokeRect(-150, -120, 300, 240);
this.settingsContainer.add(bg);
// Title
const title = this.add.text(0, -90, 'SETTINGS', { fontSize: '24px', fontStyle: 'bold', fill: '#ffffff' }).setOrigin(0.5);
this.settingsContainer.add(title);
// Close
const closeBtn = this.add.text(130, -110, 'X', { fontSize: '20px', fill: '#ff0000' }).setOrigin(0.5);
closeBtn.setInteractive({ useHandCursor: true });
closeBtn.on('pointerdown', () => this.toggleSettingsMenu());
this.settingsContainer.add(closeBtn);
// Options
let y = -40;
// 1. View Distance (Simple Toggle for now: Low/High)
this.createSettingToggle(0, y, 'VIEW DISTANCE',
() => this.gameScene.settings.viewDistance, // getter
(val) => { // setter
this.gameScene.settings.viewDistance = val;
// Apply immediately? Or next frame.
// TerrainSystem reads it in updateCulling? We need to ensure it does.
if (this.gameScene.terrainSystem) this.gameScene.terrainSystem.lastCullX = -9999; // Force update
},
['LOW', 'HIGH']
);
y += 50;
// 2. Weather Particles
this.createSettingToggle(0, y, 'PARTICLES',
() => this.gameScene.settings.particles,
(val) => {
this.gameScene.settings.particles = val;
// Restart rain if active
if (this.gameScene.weatherSystem && (this.gameScene.weatherSystem.currentWeather === 'rain' || this.gameScene.weatherSystem.currentWeather === 'storm')) {
this.gameScene.weatherSystem.startRain(this.gameScene.weatherSystem.currentWeather === 'storm');
}
},
['NONE', 'LOW', 'HIGH']
);
y += 50;
// 3. Shadows
this.createSettingToggle(0, y, 'SHADOWS',
() => this.gameScene.settings.shadows ? 'ON' : 'OFF',
(val) => {
this.gameScene.settings.shadows = (val === 'ON');
// Trigger redraw? Complex. For now just saves state.
// Maybe reload scene?
},
['ON', 'OFF']
);
}
createSettingToggle(x, y, label, getter, setter, options) {
const labelText = this.add.text(x - 80, y, label, { fontSize: '16px', fill: '#aaaaaa' }).setOrigin(1, 0.5);
this.settingsContainer.add(labelText);
const currentVal = getter();
const valueText = this.add.text(x + 50, y, currentVal, { fontSize: '16px', fill: '#ffffff', fontStyle: 'bold' }).setOrigin(0.5, 0.5);
this.settingsContainer.add(valueText);
// Click to cycle
const hitArea = this.add.rectangle(x + 50, y, 100, 30, 0xffffff, 0.1);
hitArea.setInteractive({ useHandCursor: true });
hitArea.on('pointerdown', () => {
const cur = getter();
let idx = options.indexOf(cur);
idx = (idx + 1) % options.length;
const next = options[idx];
setter(next);
valueText.setText(next);
});
this.settingsContainer.add(hitArea);
}
updateQuestTracker(quest) {
if (!this.questContainer) {
this.createQuestTracker();
}
if (!quest) {
this.questContainer.setVisible(false);
return;
}
this.questContainer.setVisible(true);
this.questTitle.setText(quest.title.toUpperCase());
let objText = '';
quest.objectives.forEach(obj => {
const status = obj.done ? '✅' : '⬜';
let desc = '';
if (obj.type === 'collect') desc = `${obj.item}: ${obj.current}/${obj.amount}`;
else if (obj.type === 'action') desc = `${obj.action}: ${obj.current}/${obj.amount}`;
else if (obj.type === 'kill') desc = `Slay ${obj.target}: ${obj.current}/${obj.amount}`;
objText += `${status} ${desc}\n`;
});
this.questObjectives.setText(objText);
}
createQuestTracker() {
const x = this.width - 240;
const y = 20;
this.questContainer = this.add.container(x, y);
// BG
const bg = this.add.graphics();
bg.fillStyle(0x000000, 0.6);
bg.fillRect(0, 0, 220, 100);
bg.lineStyle(2, 0xffaa00, 0.8);
bg.strokeRect(0, 0, 220, 100);
this.questContainer.add(bg);
this.questTrackerBg = bg; // ref to resize later if needed
// Title Header
const header = this.add.text(10, 5, 'CURRENT QUEST', { fontSize: '10px', fill: '#aaaaaa' });
this.questContainer.add(header);
// Title
this.questTitle = this.add.text(10, 20, 'Quest Title', {
fontSize: '16px', fill: '#ffaa00', fontStyle: 'bold'
});
this.questContainer.add(this.questTitle);
// Objectives
this.questObjectives = this.add.text(10, 45, 'Objectives...', {
fontSize: '14px', fill: '#ffffff', lineSpacing: 5
});
this.questContainer.add(this.questObjectives);
}
createVirtualJoystick() {
const x = 120;
const y = this.height - 120;
// Warning: this.height might not be updated on resize? Use this.scale.height or just initial.
// UIScene is usually overlay, so simple coords ok. But resize needs handling.
const r = 60;
// Visuals
this.joyBase = this.add.circle(x, y, r, 0xffffff, 0.1).setScrollFactor(0).setDepth(2000).setInteractive();
this.joyStick = this.add.circle(x, y, r / 2, 0xffffff, 0.5).setScrollFactor(0).setDepth(2001);
this.virtualJoystick = { up: false, down: false, left: false, right: false };
this.joyDragging = false;
// Events
this.input.on('pointermove', (pointer) => {
if (!this.joyDragging) return;
this.updateJoystick(pointer.x, pointer.y, x, y, r);
});
this.input.on('pointerup', () => {
if (this.joyDragging) {
this.joyDragging = false;
this.joyStick.setPosition(x, y);
this.virtualJoystick = { up: false, down: false, left: false, right: false };
}
});
this.joyBase.on('pointerdown', (pointer) => {
this.joyDragging = true;
this.updateJoystick(pointer.x, pointer.y, x, y, r);
});
}
updateJoystick(px, py, cx, cy, r) {
const angle = Phaser.Math.Angle.Between(cx, cy, px, py);
const dist = Math.min(r, Phaser.Math.Distance.Between(cx, cy, px, py));
const sx = cx + Math.cos(angle) * dist;
const sy = cy + Math.sin(angle) * dist;
this.joyStick.setPosition(sx, sy);
// Normalize angle to degrees
let deg = Phaser.Math.RadToDeg(angle);
this.virtualJoystick = { up: false, down: false, left: false, right: false };
// Mapping to Isometric direction keys
// UP Key (Top-Left on screen) -> Angle -135 (-180 to -90)
// RIGHT Key (Top-Right on screen) -> Angle -45 (-90 to 0)
// DOWN Key (Bottom-Right on screen) -> Angle 45 (0 to 90)
// LEFT Key (Bottom-Left on screen) -> Angle 135 (90 to 180)
if (deg > -170 && deg <= -80) this.virtualJoystick.up = true; // Tuned slightly
else if (deg > -80 && deg <= 10) this.virtualJoystick.right = true;
else if (deg > 10 && deg <= 100) this.virtualJoystick.down = true;
else this.virtualJoystick.left = true;
}
showQuestDialog(quest, onAccept) {
const width = 400;
const height = 250;
const x = this.cameras.main.centerX;
const y = this.cameras.main.centerY;
const container = this.add.container(x, y);
container.setDepth(5000);
const bg = this.add.rectangle(0, 0, width, height, 0x222222, 0.95);
bg.setStrokeStyle(4, 0x444444);
const title = this.add.text(0, -90, quest.title.toUpperCase(), { fontSize: '24px', fontStyle: 'bold', color: '#ffcc00' }).setOrigin(0.5);
const desc = this.add.text(0, -30, quest.description, { fontSize: '16px', color: '#dddddd', align: 'center', wordWrap: { width: width - 60 } }).setOrigin(0.5);
let rText = "Reward: ";
if (quest.reward.gold) rText += `${quest.reward.gold} G `;
if (quest.reward.item) rText += `+ ${quest.reward.amount || 1} ${quest.reward.item}`;
const reward = this.add.text(0, 40, rText, { fontSize: '16px', color: '#00ff00', fontStyle: 'italic' }).setOrigin(0.5);
// Buttons
const btnAccept = this.add.rectangle(-70, 90, 120, 40, 0x228B22).setInteractive({ useHandCursor: true });
const txtAccept = this.add.text(-70, 90, 'ACCEPT', { fontSize: '18px', fontStyle: 'bold' }).setOrigin(0.5);
const btnClose = this.add.rectangle(70, 90, 120, 40, 0x8B0000).setInteractive({ useHandCursor: true });
const txtClose = this.add.text(70, 90, 'DECLINE', { fontSize: '18px', fontStyle: 'bold' }).setOrigin(0.5);
btnAccept.on('pointerdown', () => {
onAccept();
container.destroy();
});
btnClose.on('pointerdown', () => {
container.destroy();
});
container.add([bg, title, desc, reward, btnAccept, txtAccept, btnClose, txtClose]);
// Appear anim
container.setScale(0);
this.tweens.add({ targets: container, scale: 1, duration: 200, ease: 'Back.out' });
}
toggleCollectionMenu() {
if (!this.collectionContainer) this.createCollectionMenu();
this.collectionContainer.setVisible(!this.collectionContainer.visible);
if (this.collectionContainer.visible) {
this.refreshCollection();
}
}
// --- SKILL TREE SYSTEM ---
toggleSkillTree() {
if (!this.skillTreeContainer) this.createSkillTreeMenu();
this.skillTreeContainer.setVisible(!this.skillTreeContainer.visible);
if (this.skillTreeContainer.visible) {
this.refreshSkillTree();
}
}
createSkillTreeMenu() {
const w = 600;
const h = 450;
const x = this.scale.width / 2;
const y = this.scale.height / 2;
this.skillTreeContainer = this.add.container(x, y);
this.skillTreeContainer.setDepth(2200);
// Background
const bg = this.add.graphics();
bg.fillStyle(0x002200, 0.95); // Dark Green Matrix style
bg.fillRoundedRect(-w / 2, -h / 2, w, h, 15);
bg.lineStyle(3, 0x00FF00, 1);
bg.strokeRoundedRect(-w / 2, -h / 2, w, h, 15);
this.skillTreeContainer.add(bg);
// Header
const title = this.add.text(0, -h / 2 + 30, 'HYBRID DNA EVOLUTION', {
fontSize: '24px', fontFamily: 'monospace', fontStyle: 'bold', color: '#00FF00'
}).setOrigin(0.5);
this.skillTreeContainer.add(title);
// Info Panel
this.skillPointsText = this.add.text(0, -h / 2 + 60, 'Available Points: 0', {
fontSize: '18px', fontFamily: 'monospace', color: '#FFFF00'
}).setOrigin(0.5);
this.skillTreeContainer.add(this.skillPointsText);
// Skills Container
this.skillsGrid = this.add.container(-w / 2 + 50, -h / 2 + 100);
this.skillTreeContainer.add(this.skillsGrid);
// Close Button
const closeBtn = this.add.text(w / 2 - 30, -h / 2 + 30, 'X', {
fontSize: '24px', color: '#FF0000', fontStyle: 'bold'
}).setOrigin(0.5);
closeBtn.setInteractive({ useHandCursor: true }).on('pointerdown', () => this.toggleSkillTree());
this.skillTreeContainer.add(closeBtn);
this.skillTreeContainer.setVisible(false);
}
refreshSkillTree() {
if (!this.gameScene || !this.gameScene.hybridSkillSystem) return;
const sys = this.gameScene.hybridSkillSystem;
this.skillPointsText.setText(`LEVEL: ${sys.level} | POINTS: ${sys.points}`);
this.skillsGrid.removeAll(true);
const skills = Object.entries(sys.skills);
let y = 0;
skills.forEach(([id, skill]) => {
// Bg
const rowBg = this.add.rectangle(250, y + 25, 500, 50, 0x003300);
this.skillsGrid.add(rowBg);
// Name & Level
const nameText = this.add.text(0, y + 10, `${skill.name} (Lv ${skill.level}/${skill.max})`, {
fontSize: '18px', fontFamily: 'monospace', color: '#00FF00', fontStyle: 'bold'
});
this.skillsGrid.add(nameText);
// Description
const descText = this.add.text(0, y + 32, skill.desc, {
fontSize: '12px', fontFamily: 'monospace', color: '#88FF88'
});
this.skillsGrid.add(descText);
// Upgrade Button
if (skill.level < skill.max) {
const canUpgrade = sys.points > 0;
const btnX = 450;
const btnColor = canUpgrade ? 0x00AA00 : 0x555555;
const btn = this.add.rectangle(btnX, y + 25, 80, 30, btnColor);
if (canUpgrade) {
btn.setInteractive({ useHandCursor: true });
btn.on('pointerdown', () => {
if (sys.tryUpgradeSkill(id)) {
this.refreshSkillTree(); // Update UI
}
});
}
this.skillsGrid.add(btn);
const btnText = this.add.text(btnX, y + 25, 'UPGRADE', {
fontSize: '14px', color: '#FFFFFF'
}).setOrigin(0.5);
this.skillsGrid.add(btnText);
} else {
const maxText = this.add.text(450, y + 25, 'MAXED', {
fontSize: '14px', color: '#FFFF00', fontStyle: 'bold'
}).setOrigin(0.5);
this.skillsGrid.add(maxText);
}
y += 60;
});
}
createCollectionMenu() {
const w = 500;
const h = 400;
const x = this.scale.width / 2;
const y = this.scale.height / 2;
this.collectionContainer = this.add.container(x, y);
this.collectionContainer.setDepth(2100);
// Book Background
const bg = this.add.graphics();
bg.fillStyle(0x3e2723, 1); // Dark brown book
bg.fillRoundedRect(-w / 2, -h / 2, w, h, 10);
bg.lineStyle(4, 0xdec20b, 1); // Gold trim
bg.strokeRoundedRect(-w / 2, -h / 2, w, h, 10);
// Pages
bg.fillStyle(0xf5e6c8, 1); // Paper color
bg.fillRoundedRect(-w / 2 + 20, -h / 2 + 20, w - 40, h - 40, 5);
this.collectionContainer.add(bg);
// Title
const title = this.add.text(0, -h / 2 + 40, 'COLLECTION ALBUM', {
fontSize: '28px',
fontFamily: 'serif',
color: '#3e2723',
fontStyle: 'bold'
}).setOrigin(0.5);
this.collectionContainer.add(title);
// Grid Container
this.collectionGrid = this.add.container(-w / 2 + 40, -h / 2 + 80);
this.collectionContainer.add(this.collectionGrid);
// Close Button
const closeBtn = this.add.text(w / 2 - 40, -h / 2 + 40, 'X', {
fontSize: '24px', color: '#ff0000', fontStyle: 'bold'
}).setOrigin(0.5);
closeBtn.setInteractive({ useHandCursor: true })
.on('pointerdown', () => this.toggleCollectionMenu());
this.collectionContainer.add(closeBtn);
this.collectionContainer.setVisible(false);
}
refreshCollection() {
if (!this.gameScene || !this.gameScene.collectionSystem) return;
this.collectionGrid.removeAll(true);
const system = this.gameScene.collectionSystem;
const items = Object.entries(system.items);
// Display stats
const progress = system.getProgress();
const statText = this.add.text(0, 340, `Collected: ${progress.unlocked} / ${progress.total} (${Math.round(progress.percent)}%)`, {
fontSize: '16px', color: '#3e2723', fontStyle: 'italic'
}).setOrigin(0.5);
this.collectionContainer.add(statText); // Needs to be added to container, not grid
// Grid Layout
let col = 0;
let row = 0;
const visibleCols = 6;
const cellSize = 60;
items.forEach(([id, data]) => {
const isUnlocked = system.has(id);
const cx = col * cellSize;
const cy = row * cellSize;
// Slot Bg
const slot = this.add.rectangle(cx, cy, 50, 50, isUnlocked ? 0xccb08e : 0xaaaaaa);
slot.setStrokeStyle(1, 0x8d6e63);
this.collectionGrid.add(slot);
if (isUnlocked) {
// Icon
const key = `item_${id}`;
const tex = this.textures.exists(key) ? key : null;
if (tex) {
const sprite = this.add.sprite(cx, cy, tex).setScale(1.2);
this.collectionGrid.add(sprite);
} else {
this.collectionGrid.add(this.add.text(cx, cy, id.substr(0, 2), { fontSize: '12px', color: '#000' }).setOrigin(0.5));
}
// Tooltip logic could go here (hover)
slot.setInteractive();
slot.on('pointerover', () => {
this.showCollectionTooltip(cx, cy, data);
});
slot.on('pointerout', () => {
this.hideCollectionTooltip();
});
} else {
// Locked
this.collectionGrid.add(this.add.text(cx, cy, '?', { fontSize: '24px', color: '#555555' }).setOrigin(0.5));
}
col++;
if (col >= visibleCols) {
col = 0;
row++;
}
});
}
showCollectionTooltip(x, y, data) {
if (this.collectionTooltip) this.collectionTooltip.destroy();
this.collectionTooltip = this.add.container(this.collectionGrid.x + x + 30, this.collectionGrid.y + y);
this.collectionContainer.add(this.collectionTooltip);
const bg = this.add.rectangle(0, 0, 150, 60, 0x000000, 0.8);
const name = this.add.text(0, -15, data.name, { fontSize: '14px', fontStyle: 'bold', color: '#fff' }).setOrigin(0.5);
const desc = this.add.text(0, 10, data.category, { fontSize: '12px', color: '#aaa' }).setOrigin(0.5);
this.collectionTooltip.add([bg, name, desc]);
}
hideCollectionTooltip() {
if (this.collectionTooltip) {
this.collectionTooltip.destroy();
this.collectionTooltip = null;
}
}
// --- WORKER MENU ---
showWorkerMenu(zombie) {
if (this.workerMenuContainer) this.workerMenuContainer.destroy();
const x = this.scale.width / 2;
const y = this.scale.height / 2;
const w = 300;
const h = 350;
this.workerMenuContainer = this.add.container(x, y);
this.workerMenuContainer.setDepth(2300);
// Background
const bg = this.add.graphics();
bg.fillStyle(0x333333, 0.95);
bg.fillRoundedRect(-w / 2, -h / 2, w, h, 10);
bg.lineStyle(2, 0x00FF00, 1);
bg.strokeRoundedRect(-w / 2, -h / 2, w, h, 10);
this.workerMenuContainer.add(bg);
// Title
const title = this.add.text(0, -h / 2 + 25, 'ZOMBIE WORKER CONTROL', {
fontSize: '20px', fontStyle: 'bold', color: '#00FF00'
}).setOrigin(0.5);
this.workerMenuContainer.add(title);
// Status
const currentTask = zombie.workerData ? zombie.workerData.type : 'IDLE';
const statusText = this.add.text(0, -h / 2 + 55, `Current Task: ${currentTask}`, {
fontSize: '16px', color: '#FFFFFF'
}).setOrigin(0.5);
this.workerMenuContainer.add(statusText);
// Buttons
const buttons = [
{ label: 'FARM (Seeds/Harvest)', action: 'FARM', color: 0x8B4513 },
{ label: 'MINE (Rocks)', action: 'MINE', color: 0x555555 },
{ label: 'CLEAR ZONE (Trees/Debris)', action: 'CLEAR', color: 0xAA0000 },
{ label: 'STOP / UNASSIGN', action: 'STOP', color: 0xFF5555 }
];
let btnY = -h / 2 + 100;
buttons.forEach(btn => {
const btnBg = this.add.rectangle(0, btnY, 250, 40, btn.color);
btnBg.setInteractive({ useHandCursor: true });
btnBg.on('pointerdown', () => {
this.assignZombieTask(zombie, btn.action);
this.workerMenuContainer.destroy();
this.workerMenuContainer = null;
});
this.workerMenuContainer.add(btnBg);
const txt = this.add.text(0, btnY, btn.label, { fontSize: '16px', fontStyle: 'bold' }).setOrigin(0.5);
this.workerMenuContainer.add(txt);
btnY += 50;
});
// Close
const closeBtn = this.add.text(w / 2 - 20, -h / 2 + 20, 'X', { fontSize: '20px', color: '#FF0000', fontStyle: 'bold' }).setOrigin(0.5);
closeBtn.setInteractive({ useHandCursor: true }).on('pointerdown', () => {
this.workerMenuContainer.destroy();
this.workerMenuContainer = null;
});
this.workerMenuContainer.add(closeBtn);
}
assignZombieTask(zombie, task) {
if (!this.gameScene || !this.gameScene.zombieWorkerSystem) return;
if (task === 'STOP') {
this.gameScene.zombieWorkerSystem.removeWorker(zombie);
this.gameScene.events.emit('show-floating-text', {
x: zombie.sprite.x, y: zombie.sprite.y - 50, text: 'Idling...', color: '#FFFFFF'
});
} else {
// Assign with radius 6
this.gameScene.zombieWorkerSystem.assignWork(zombie, task, 8);
this.gameScene.events.emit('show-floating-text', {
x: zombie.sprite.x, y: zombie.sprite.y - 50, text: `Task: ${task}`, color: '#00FF00'
});
}
}
}