Files
novafarma/src/systems/BuildSystem.js
2025-12-12 10:17:21 +01:00

543 lines
17 KiB
JavaScript

class BuildSystem {
constructor(scene) {
this.scene = scene;
this.buildMode = false;
this.selectedBuilding = 'fence';
this.previewSprite = null;
this.placedBuildings = [];
// Building definitions
this.buildings = {
'fence': {
name: 'Fence (Old)',
textureKey: 'fence_isometric',
cost: { wood: 2 },
collision: false,
scale: 0.3
},
'fence_post': {
name: 'Fence Post',
textureKey: 'fence_post',
cost: { wood: 1 },
collision: false,
scale: 0.2
},
'fence_horizontal': {
name: 'Fence →',
textureKey: 'fence_horizontal',
cost: { wood: 2 },
collision: false,
scale: 0.2
},
'fence_vertical': {
name: 'Fence ↓',
textureKey: 'fence_vertical',
cost: { wood: 2 },
collision: false,
scale: 0.2
},
'fence_corner': {
name: 'Fence ⌞',
textureKey: 'fence_corner',
cost: { wood: 2 },
collision: false,
scale: 0.2
},
'barn': {
name: 'Barn',
textureKey: 'barn_isometric',
cost: { wood: 40, stone: 20 },
collision: true,
scale: 0.5
},
'grave': {
name: 'Grave',
textureKey: 'grave_zombie',
cost: { stone: 10 },
collision: false,
scale: 0.3
},
'farmhouse': {
name: 'Farmhouse',
textureKey: 'farmhouse_isometric',
cost: { wood: 50, stone: 30, gold: 100 },
collision: true,
scale: 0.5
},
'blacksmith': {
name: 'Blacksmith',
textureKey: 'blacksmith_workshop',
cost: { wood: 30, stone: 40, gold: 80 },
collision: true,
scale: 0.45
}
};
}
toggleBuildMode() {
this.buildMode = !this.buildMode;
console.log(`Build Mode: ${this.buildMode ? 'ON' : 'OFF'}`);
// Show/hide preview
if (this.buildMode) {
this.createPreview();
this.showBuildUI(); // Dodaj UI
} else {
this.destroyPreview();
this.hideBuildUI(); // Skrij UI
}
return this.buildMode;
}
selectBuilding(buildingId) {
if (!this.buildings[buildingId]) return;
this.selectedBuilding = buildingId;
// Play UI click sound
if (this.scene.soundManager) {
this.scene.soundManager.playUIClick();
}
// Refresh preview
if (this.buildMode) {
this.destroyPreview();
this.createPreview();
this.updateBuildUI(); // Posodobi UI
}
}
showBuildUI() {
const uiScene = this.scene.scene.get('UIScene');
if (!uiScene) return;
const width = this.scene.cameras.main.width;
const height = this.scene.cameras.main.height;
// Ustvari UI container
this.buildUIContainer = uiScene.add.container(width - 250, 100);
this.buildUIContainer.setDepth(9999);
this.buildUIContainer.setScrollFactor(0);
// Ozadje panela
const bg = uiScene.add.rectangle(0, 0, 220, 400, 0x1a1a2e, 0.95);
bg.setStrokeStyle(3, 0x00ff41);
this.buildUIContainer.add(bg);
// Naslov
const title = uiScene.add.text(0, -180, '🏗️ BUILD MODE', {
fontSize: '20px',
fontFamily: 'Courier New',
color: '#00ff41',
fontStyle: 'bold'
}).setOrigin(0.5);
this.buildUIContainer.add(title);
// Izbrana stavba
this.selectedBuildingText = uiScene.add.text(0, -150, '', {
fontSize: '14px',
fontFamily: 'Courier New',
color: '#ffffff',
align: 'center'
}).setOrigin(0.5);
this.buildUIContainer.add(this.selectedBuildingText);
// Cena
this.costText = uiScene.add.text(0, -120, '', {
fontSize: '12px',
fontFamily: 'Courier New',
color: '#ffaa00',
align: 'center'
}).setOrigin(0.5);
this.buildUIContainer.add(this.costText);
// Kontrole
const controls = [
'━━━━━━━━━━━━━━',
'CONTROLS:',
'',
'1 - Fence Post',
'2 - Fence →',
'3 - Fence ↓',
'4 - Fence ⌞',
'5 - Barn',
'',
'Click - Place',
'B - Exit',
'━━━━━━━━━━━━━━'
];
const controlsText = uiScene.add.text(0, 0, controls.join('\n'), {
fontSize: '12px',
fontFamily: 'Courier New',
color: '#aaaaaa',
align: 'center',
lineSpacing: 4
}).setOrigin(0.5);
this.buildUIContainer.add(controlsText);
// Status
this.statusText = uiScene.add.text(0, 170, '', {
fontSize: '11px',
fontFamily: 'Courier New',
color: '#ffffff',
align: 'center'
}).setOrigin(0.5);
this.buildUIContainer.add(this.statusText);
this.updateBuildUI();
}
updateBuildUI() {
if (!this.buildUIContainer) return;
const building = this.buildings[this.selectedBuilding];
// Posodobi ime
this.selectedBuildingText.setText(`Selected:\n${building.name}`);
// Posodobi ceno
let costStr = 'Cost: ';
const costs = [];
for (const [resource, amount] of Object.entries(building.cost)) {
if (resource === 'gold') {
costs.push(`${amount} Gold`);
} else {
costs.push(`${amount} ${resource}`);
}
}
costStr += costs.join(', ');
this.costText.setText(costStr);
// Preveri vire
const hasResources = this.hasResources(building.cost);
this.statusText.setText(hasResources ? '✅ Ready to build' : '❌ Not enough resources');
this.statusText.setColor(hasResources ? '#00ff41' : '#ff0000');
}
hideBuildUI() {
if (this.buildUIContainer) {
this.buildUIContainer.destroy();
this.buildUIContainer = null;
}
}
createPreview() {
const building = this.buildings[this.selectedBuilding];
if (!this.scene.textures.exists(building.textureKey)) {
console.warn(`Texture not found: ${building.textureKey}`);
return;
}
this.previewSprite = this.scene.add.sprite(0, 0, building.textureKey);
this.previewSprite.setOrigin(0.5, 1);
this.previewSprite.setAlpha(0.6);
this.previewSprite.setDepth(10000); // Always on top
this.previewSprite.setScale(building.scale || 1.0);
}
destroyPreview() {
if (this.previewSprite) {
this.previewSprite.destroy();
this.previewSprite = null;
}
}
update() {
if (!this.buildMode || !this.previewSprite) return;
// Follow mouse
const pointer = this.scene.input.activePointer;
const worldPoint = this.scene.cameras.main.getWorldPoint(pointer.x, pointer.y);
const gridPos = this.scene.iso.toGrid(worldPoint.x, worldPoint.y);
const screenPos = this.scene.iso.toScreen(gridPos.x, gridPos.y);
this.previewSprite.setPosition(screenPos.x, screenPos.y);
// Check if can place
const canPlace = this.canPlaceAt(gridPos.x, gridPos.y);
this.previewSprite.setTint(canPlace ? 0x00ff00 : 0xff0000);
// Place on click
if (pointer.isDown && canPlace) {
this.placeBuilding(gridPos.x, gridPos.y);
}
}
canPlaceAt(gridX, gridY) {
// Check if already has building
const exists = this.placedBuildings.find(b => b.gridX === gridX && b.gridY === gridY);
if (exists) return false;
// Check terrain (only on farm tiles)
if (!this.scene.terrainSystem) return false;
const tile = this.scene.terrainSystem.getTile(gridX, gridY);
if (!tile || tile.type !== 'farm') return false;
// Check cost
const building = this.buildings[this.selectedBuilding];
if (!this.hasResources(building.cost)) return false;
return true;
}
hasResources(cost) {
const inv = this.scene.inventorySystem;
if (!inv) return false;
for (const [resource, amount] of Object.entries(cost)) {
if (resource === 'gold') {
if (inv.gold < amount) return false;
} else {
if (!inv.hasItem(resource, amount)) return false;
}
}
return true;
}
placeBuilding(gridX, gridY) {
const building = this.buildings[this.selectedBuilding];
// Consume resources
const inv = this.scene.inventorySystem;
for (const [resource, amount] of Object.entries(building.cost)) {
if (resource === 'gold') {
inv.gold -= amount;
} else {
inv.removeItem(resource, amount);
}
}
// Create sprite
const screenPos = this.scene.iso.toScreen(gridX, gridY);
const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, building.textureKey);
sprite.setOrigin(0.5, 1);
sprite.setScale(building.scale || 1.0);
sprite.setDepth(this.scene.iso.getDepth(gridX, gridY) + 5); // Above terrain
// Store
this.placedBuildings.push({
gridX,
gridY,
type: this.selectedBuilding,
sprite,
collision: building.collision
});
// Play build sound
if (this.scene.soundManager) {
this.scene.soundManager.playBuild();
}
console.log(`🏗️ Placed ${building.name} at (${gridX}, ${gridY})`);
// Update UI
this.scene.events.emit('update-inventory');
}
isCollisionAt(gridX, gridY) {
const building = this.placedBuildings.find(b => b.gridX === gridX && b.gridY === gridY);
return building ? building.collision : false;
}
/**
* Ročno postavi ograjo na natančno koordinato.
* @param {number} tileX - X koordinata ploščice (0 do 99).
* @param {number} tileY - Y koordinata ploščice (0 do 99).
* @param {string} fenceType - Tip ograje: 'fence', 'fence_post', 'fence_horizontal', 'fence_vertical', 'fence_corner'
* @param {boolean} consumeResources - Ali naj porabi vire (privzeto: false)
* @returns {boolean} - True če je bila ograja uspešno postavljena
*/
placeSingleFence(tileX, tileY, fenceType = 'fence_horizontal', consumeResources = false) {
// 1. Preveri, ali je lokacija znotraj mape
if (tileX < 0 || tileX >= 100 || tileY < 0 || tileY >= 100) {
console.error(`❌ Poskus postavitve ograje izven robov mape: (${tileX}, ${tileY})`);
return false;
}
// 2. Preveri, ali tip ograje obstaja
if (!this.buildings[fenceType]) {
console.error(`❌ Neznan tip ograje: ${fenceType}`);
return false;
}
// 3. Preveri, ali že obstaja zgradba na tej lokaciji
const exists = this.placedBuildings.find(b => b.gridX === tileX && b.gridY === tileY);
if (exists) {
console.warn(`⚠️ Na lokaciji (${tileX}, ${tileY}) že obstaja zgradba.`);
return false;
}
const building = this.buildings[fenceType];
// 4. Preveri in porabi vire (če je zahtevano)
if (consumeResources) {
if (!this.hasResources(building.cost)) {
console.warn(`⚠️ Ni dovolj virov za postavitev ${building.name}`);
return false;
}
const inv = this.scene.inventorySystem;
for (const [resource, amount] of Object.entries(building.cost)) {
if (resource === 'gold') {
inv.gold -= amount;
} else {
inv.removeItem(resource, amount);
}
}
}
// 5. Ustvari sprite
const screenPos = this.scene.iso.toScreen(tileX, tileY);
const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, building.textureKey);
sprite.setOrigin(0.5, 1);
sprite.setScale(building.scale || 1.0);
sprite.setDepth(this.scene.iso.getDepth(tileX, tileY) + 5); // Nad terenom
// 6. Shrani
this.placedBuildings.push({
gridX: tileX,
gridY: tileY,
type: fenceType,
sprite,
collision: building.collision
});
console.log(`${building.name} postavljena na (${tileX}, ${tileY})`);
// 7. Posodobi UI (če so bili porabljeni viri)
if (consumeResources) {
this.scene.events.emit('update-inventory');
}
return true;
}
/**
* Postavi linijo ograj med dvema točkama.
* @param {number} startX - Začetna X koordinata
* @param {number} startY - Začetna Y koordinata
* @param {number} endX - Končna X koordinata
* @param {number} endY - Končna Y koordinata
* @param {string} fenceType - Tip ograje
* @param {boolean} consumeResources - Ali naj porabi vire
*/
placeFenceLine(startX, startY, endX, endY, fenceType = 'fence_horizontal', consumeResources = false) {
const dx = Math.abs(endX - startX);
const dy = Math.abs(endY - startY);
const sx = startX < endX ? 1 : -1;
const sy = startY < endY ? 1 : -1;
let err = dx - dy;
let x = startX;
let y = startY;
while (true) {
this.placeSingleFence(x, y, fenceType, consumeResources);
if (x === endX && y === endY) break;
const e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x += sx;
}
if (e2 < dx) {
err += dx;
y += sy;
}
}
console.log(`🔗 Linija ograj postavljena od (${startX}, ${startY}) do (${endX}, ${endY})`);
}
/**
* Postavi pravokotnik ograj.
* @param {number} x - Levi zgornji X
* @param {number} y - Levi zgornji Y
* @param {number} width - Širina
* @param {number} height - Višina
* @param {string} fenceType - Tip ograje
* @param {boolean} consumeResources - Ali naj porabi vire
*/
placeFenceRectangle(x, y, width, height, fenceType = 'fence_horizontal', consumeResources = false) {
// Zgornja linija
for (let i = 0; i < width; i++) {
this.placeSingleFence(x + i, y, fenceType, consumeResources);
}
// Spodnja linija
for (let i = 0; i < width; i++) {
this.placeSingleFence(x + i, y + height - 1, fenceType, consumeResources);
}
// Leva linija
for (let i = 1; i < height - 1; i++) {
this.placeSingleFence(x, y + i, fenceType, consumeResources);
}
// Desna linija
for (let i = 1; i < height - 1; i++) {
this.placeSingleFence(x + width - 1, y + i, fenceType, consumeResources);
}
console.log(`📦 Pravokotnik ograj postavljen na (${x}, ${y}) velikosti ${width}x${height}`);
}
showTutorial() {
const uiScene = this.scene.scene.get('UIScene');
if (!uiScene) return;
const width = this.scene.cameras.main.width;
const height = this.scene.cameras.main.height;
// Tutorial panel
const panel = uiScene.add.container(width / 2, height / 2);
panel.setDepth(10000);
const bg = uiScene.add.rectangle(0, 0, 500, 300, 0x1a1a2e, 0.95);
bg.setStrokeStyle(3, 0x00ff41);
panel.add(bg);
const title = uiScene.add.text(0, -120, '🏗️ BUILD MODE', {
fontSize: '24px',
fontFamily: 'Courier New',
color: '#00ff41',
fontStyle: 'bold'
}).setOrigin(0.5);
panel.add(title);
const instructions = [
'Controls:',
'1-5: Select building type',
'Mouse: Move preview',
'Click: Place building',
'B: Exit build mode',
'',
'Green = OK | Red = Blocked'
];
const text = uiScene.add.text(0, 0, instructions.join('\n'), {
fontSize: '16px',
fontFamily: 'Courier New',
color: '#ffffff',
align: 'center',
lineSpacing: 8
}).setOrigin(0.5);
panel.add(text);
const closeBtn = uiScene.add.text(0, 120, '[Click to close]', {
fontSize: '14px',
color: '#888888'
}).setOrigin(0.5);
panel.add(closeBtn);
// Auto-dismiss after 5 seconds
uiScene.time.delayedCall(5000, () => {
panel.destroy();
});
// Click to dismiss
bg.setInteractive();
bg.on('pointerdown', () => {
panel.destroy();
});
}
}