543 lines
17 KiB
JavaScript
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();
|
|
});
|
|
}
|
|
}
|