Files
novafarma/EMERGENCY_SYSTEMS_RECOVERY/DefenseSystem.js
2026-01-16 02:43:46 +01:00

550 lines
16 KiB
JavaScript

/**
* DEFENSE SYSTEM - City Walls & Watchtowers
* Mrtva Dolina - Obrambe pred nomadskimi roparji
*
* Features:
* - 3-tier wall system (wooden → stone → fortress)
* - Watchtowers with Line of Sight (LoS) expansion
* - Raid detection and prevention
* - Patrol system
* - Damage and repair mechanics
*/
export class DefenseSystem {
constructor(scene) {
this.scene = scene;
// Wall configurations
this.wallTiers = {
wooden: {
name: 'Leseno Obzidje',
tier: 1,
health: 100,
defense: 30,
cost: { wood: 50, stone: 10 },
buildTime: 30000, // 30 seconds
sprite: 'wall_wooden'
},
stone: {
name: 'Kamnito Obzidje',
tier: 2,
health: 300,
defense: 70,
cost: { wood: 20, stone: 100, steel: 10 },
buildTime: 60000, // 1 minute
sprite: 'wall_stone'
},
fortress: {
name: 'Futuristično Obzidje',
tier: 3,
health: 1000,
defense: 95,
cost: { steel: 150, glass: 50, tech_parts: 20 },
buildTime: 120000, // 2 minutes
sprite: 'wall_fortress'
}
};
// Watchtower configuration
this.watchtowerConfig = {
name: 'Opazovalni Stolp',
health: 200,
losRange: 500, // Line of Sight range in pixels
detectionBonus: 0.5, // 50% earlier raid detection
cost: { wood: 30, stone: 20 },
buildTime: 45000, // 45 seconds
sprite: 'watchtower'
};
// Placed walls and towers
this.walls = [];
this.watchtowers = [];
// Patrol system
this.patrols = [];
this.patrolsUnlocked = false;
// Raid tracking
this.activeRaids = [];
this.cityDefenseRating = 0;
this.init();
}
init() {
// Listen for raid events
this.scene.events.on('raid:incoming', this.onRaidIncoming, this);
this.scene.events.on('raid:attack', this.onRaidAttack, this);
// Start passive defense calculations
this.startDefenseUpdates();
console.log('✅ DefenseSystem initialized');
}
/**
* BUILD WALL SEGMENT
*/
buildWall(x, y, tier = 'wooden', direction = 'horizontal') {
const wallConfig = this.wallTiers[tier];
if (!wallConfig) {
console.error(`Unknown wall tier: ${tier}`);
return null;
}
// Check if player has resources
if (!this.hasResources(wallConfig.cost)) {
this.scene.events.emit('show-notification', {
title: '❌ Ni Materialov',
message: `Rabiš: ${this.formatCost(wallConfig.cost)}`,
icon: '🏗️',
duration: 3000,
color: '#FF4444'
});
return null;
}
// Deduct resources
this.deductResources(wallConfig.cost);
// Create wall object
const wall = {
id: `wall_${Date.now()}`,
x: x,
y: y,
tier: tier,
direction: direction, // horizontal or vertical
health: wallConfig.health,
maxHealth: wallConfig.health,
defense: wallConfig.defense,
sprite: null,
isBuilding: true
};
// Show construction animation
this.startConstruction(wall, wallConfig);
this.walls.push(wall);
console.log(`🏗️ Building ${wallConfig.name} at (${x}, ${y})`);
return wall;
}
startConstruction(wall, config) {
// Show construction sprite
const constructionSite = this.scene.add.sprite(wall.x, wall.y, 'construction_scaffold');
constructionSite.setDepth(10);
wall.constructionSprite = constructionSite;
// Construction timer
setTimeout(() => {
this.completeWallConstruction(wall, config);
}, config.buildTime);
// Show progress bar
this.showConstructionProgress(wall, config.buildTime);
}
completeWallConstruction(wall, config) {
// Remove construction sprite
if (wall.constructionSprite) {
wall.constructionSprite.destroy();
}
// Place actual wall
const wallSprite = this.scene.add.sprite(wall.x, wall.y, config.sprite);
wallSprite.setDepth(5);
if (wall.direction === 'vertical') {
wallSprite.setRotation(Math.PI / 2); // 90 degrees
}
wall.sprite = wallSprite;
wall.isBuilding = false;
// Update defense rating
this.updateDefenseRating();
// Show completion notification
this.scene.events.emit('show-notification', {
title: '✅ Obzidje Zgrajeno',
message: `${config.name} je končano!`,
icon: '🏰',
duration: 3000,
color: '#00FF00'
});
console.log(`${config.name} construction complete`);
}
showConstructionProgress(wall, duration) {
// Progress bar above construction site
const progressBg = this.scene.add.graphics();
progressBg.fillStyle(0x000000, 0.7);
progressBg.fillRect(wall.x - 30, wall.y - 40, 60, 8);
const progressBar = this.scene.add.graphics();
wall.progressBar = progressBar;
wall.progressBg = progressBg;
const startTime = Date.now();
const progressInterval = setInterval(() => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
progressBar.clear();
progressBar.fillStyle(0x00FF00, 1);
progressBar.fillRect(wall.x - 29, wall.y - 39, 58 * progress, 6);
if (progress >= 1) {
clearInterval(progressInterval);
progressBg.destroy();
progressBar.destroy();
}
}, 100);
}
/**
* BUILD WATCHTOWER
*/
buildWatchtower(x, y) {
const config = this.watchtowerConfig;
if (!this.hasResources(config.cost)) {
this.scene.events.emit('show-notification', {
title: '❌ Ni Materialov',
message: `Rabiš: ${this.formatCost(config.cost)}`,
icon: '🗼',
duration: 3000,
color: '#FF4444'
});
return null;
}
this.deductResources(config.cost);
const tower = {
id: `tower_${Date.now()}`,
x: x,
y: y,
health: config.health,
maxHealth: config.health,
losRange: config.losRange,
detectionBonus: config.detectionBonus,
sprite: null,
isBuilding: true,
losCircle: null
};
// Start construction
this.startConstruction(tower, config);
// Complete construction
setTimeout(() => {
this.completeWatchtowerConstruction(tower);
}, config.buildTime);
this.watchtowers.push(tower);
console.log(`🗼 Building Watchtower at (${x}, ${y})`);
return tower;
}
completeWatchtowerConstruction(tower) {
if (tower.constructionSprite) {
tower.constructionSprite.destroy();
}
// Place tower sprite
const towerSprite = this.scene.add.sprite(tower.x, tower.y, this.watchtowerConfig.sprite);
towerSprite.setDepth(15);
tower.sprite = towerSprite;
tower.isBuilding = false;
// Create Line of Sight circle
this.createLosIndicator(tower);
// Update defense
this.updateDefenseRating();
this.scene.events.emit('show-notification', {
title: '✅ Stolp Zgrajen',
message: 'Opazovalni stolp povečuje vidno polje!',
icon: '🗼',
duration: 3000,
color: '#00FF00'
});
console.log(`✅ Watchtower construction complete`);
}
createLosIndicator(tower) {
// Visual LoS circle
const losCircle = this.scene.add.graphics();
losCircle.lineStyle(2, 0xFFFF00, 0.3);
losCircle.strokeCircle(tower.x, tower.y, tower.losRange);
losCircle.setDepth(1);
tower.losCircle = losCircle;
// Pulse animation
this.scene.tweens.add({
targets: losCircle,
alpha: 0.5,
duration: 2000,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut'
});
}
/**
* RAID DETECTION & DEFENSE
*/
onRaidIncoming(raidData) {
// Check if watchtowers can detect early
const earlyDetection = this.checkEarlyDetection(raidData);
if (earlyDetection) {
// Give player extra time to prepare
raidData.timeToArrival *= (1 + this.watchtowerConfig.detectionBonus);
this.scene.events.emit('show-notification', {
title: '🚨 RAID OPAŽEN!',
message: `Opazovalni stolp je opazil roparje! +${Math.floor(this.watchtowerConfig.detectionBonus * 100)}% časa za pripravo!`,
icon: '🗼',
duration: 5000,
color: '#FFAA00'
});
}
this.activeRaids.push(raidData);
console.log(`🚨 Raid incoming: ${raidData.strength} strength`);
}
checkEarlyDetection(raidData) {
// Check if any watchtower can detect the raid
for (const tower of this.watchtowers) {
if (tower.isBuilding) continue;
const distance = Phaser.Math.Distance.Between(
tower.x, tower.y,
raidData.x, raidData.y
);
if (distance <= tower.losRange) {
return true;
}
}
return false;
}
onRaidAttack(raidData) {
// Calculate defense vs attack
const raidStrength = raidData.strength;
const cityDefense = this.cityDefenseRating;
console.log(`⚔️ Raid Attack: ${raidStrength} vs Defense: ${cityDefense}`);
if (cityDefense >= raidStrength) {
// City defense holds!
this.raidRepelled(raidData);
} else {
// Walls take damage
this.wallsDamaged(raidStrength - cityDefense);
}
}
raidRepelled(raidData) {
this.activeRaids = this.activeRaids.filter(r => r.id !== raidData.id);
this.scene.events.emit('show-notification', {
title: '✅ RAID ODBIJEN!',
message: 'Obzidja so ustavila roparje!',
icon: '🛡️',
duration: 5000,
color: '#00FF00'
});
// Reward for successful defense
if (this.scene.inventorySystem) {
this.scene.inventorySystem.addGold(raidData.strength * 10);
}
console.log(`✅ Raid repelled successfully`);
}
wallsDamaged(damage) {
// Damage weakest walls first
const sortedWalls = [...this.walls].sort((a, b) => a.health - b.health);
let remainingDamage = damage;
for (const wall of sortedWalls) {
if (remainingDamage <= 0) break;
if (wall.isBuilding) continue;
const damageToWall = Math.min(wall.health, remainingDamage);
wall.health -= damageToWall;
remainingDamage -= damageToWall;
// Visual damage
if (wall.sprite) {
wall.sprite.setTint(0xFF4444);
setTimeout(() => wall.sprite.clearTint(), 500);
}
// Wall destroyed
if (wall.health <= 0) {
this.destroyWall(wall);
}
}
this.scene.events.emit('show-notification', {
title: '⚠️ Obzidja Poškodovana',
message: `Raid je povzročil ${damage} škode!`,
icon: '💥',
duration: 4000,
color: '#FF4444'
});
this.updateDefenseRating();
}
destroyWall(wall) {
if (wall.sprite) {
// Destruction animation
this.scene.tweens.add({
targets: wall.sprite,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5,
duration: 500,
onComplete: () => wall.sprite.destroy()
});
}
this.walls = this.walls.filter(w => w.id !== wall.id);
console.log(`💥 Wall destroyed: ${wall.id}`);
}
/**
* PATROL SYSTEM
*/
unlockPatrols() {
this.patrolsUnlocked = true;
console.log('✅ Patrol system unlocked');
}
createPatrol(route) {
if (!this.patrolsUnlocked) {
console.warn('Patrols not unlocked yet');
return null;
}
const patrol = {
id: `patrol_${Date.now()}`,
route: route, // Array of {x, y} waypoints
currentWaypoint: 0,
guards: [],
active: true
};
this.patrols.push(patrol);
return patrol;
}
/**
* DEFENSE RATING CALCULATION
*/
updateDefenseRating() {
let rating = 0;
// Add wall defense
this.walls.forEach(wall => {
if (!wall.isBuilding) {
const healthPercent = wall.health / wall.maxHealth;
rating += this.wallTiers[wall.tier].defense * healthPercent;
}
});
// Add watchtower bonus
rating += this.watchtowers.filter(t => !t.isBuilding).length * 10;
// Add patrol bonus
rating += this.patrols.filter(p => p.active).length * 5;
this.cityDefenseRating = Math.floor(rating);
// Emit update
this.scene.events.emit('defense:rating_updated', this.cityDefenseRating);
console.log(`🛡️ Defense Rating: ${this.cityDefenseRating}`);
}
startDefenseUpdates() {
// Update defense every 5 seconds
setInterval(() => {
this.updateDefenseRating();
}, 5000);
}
/**
* UTILITY FUNCTIONS
*/
hasResources(cost) {
if (!this.scene.inventorySystem) return true; // Dev mode
for (const [resource, amount] of Object.entries(cost)) {
if (!this.scene.inventorySystem.hasItem(resource, amount)) {
return false;
}
}
return true;
}
deductResources(cost) {
if (!this.scene.inventorySystem) return;
for (const [resource, amount] of Object.entries(cost)) {
this.scene.inventorySystem.removeItem(resource, amount);
}
}
formatCost(cost) {
return Object.entries(cost)
.map(([resource, amount]) => `${amount}x ${resource}`)
.join(', ');
}
/**
* GET STATUS FOR UI
*/
getDefenseStatus() {
return {
rating: this.cityDefenseRating,
walls: this.walls.length,
watchtowers: this.watchtowers.length,
patrols: this.patrols.length,
activeRaids: this.activeRaids.length
};
}
destroy() {
this.walls.forEach(wall => {
if (wall.sprite) wall.sprite.destroy();
if (wall.losCircle) wall.losCircle.destroy();
});
this.watchtowers.forEach(tower => {
if (tower.sprite) tower.sprite.destroy();
if (tower.losCircle) tower.losCircle.destroy();
});
this.walls = [];
this.watchtowers = [];
this.patrols = [];
}
}