393 lines
11 KiB
JavaScript
393 lines
11 KiB
JavaScript
/**
|
|
* FARM RAID SYSTEM
|
|
* Spawn raider waves, defend farm, rewards
|
|
* Integrates with DefenseSystem and NomadRaiderAI
|
|
*/
|
|
|
|
export class FarmRaidSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
|
|
// Raid state
|
|
this.isRaidActive = false;
|
|
this.currentWave = 0;
|
|
this.totalWaves = 0;
|
|
this.raidDifficulty = 1;
|
|
|
|
// Spawn management
|
|
this.activeRaiders = [];
|
|
this.spawnPoints = [];
|
|
|
|
// Raid triggers
|
|
this.fameThreshold = 500; // Trigger raid at 500 fame
|
|
this.resourceThreshold = 5000; // Or 5000 resources
|
|
this.timeBetweenRaids = 600000; // 10 minutes minimum
|
|
this.lastRaidTime = 0;
|
|
|
|
// Rewards
|
|
this.defenseRewards = new Map();
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.initializeSpawnPoints();
|
|
this.initializeRewards();
|
|
|
|
// Check for raid triggers periodically
|
|
this.scene.time.addEvent({
|
|
delay: 30000, // Check every 30s
|
|
callback: () => this.checkRaidTriggers(),
|
|
loop: true
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize raid spawn points around farm
|
|
*/
|
|
initializeSpawnPoints() {
|
|
const farmCenter = { x: 400, y: 300 }; // Adjust to actual farm center
|
|
const spawnDistance = 400;
|
|
|
|
// 8 spawn points around farm (N, NE, E, SE, S, SW, W, NW)
|
|
for (let angle = 0; angle < 360; angle += 45) {
|
|
const rad = Phaser.Math.DegToRad(angle);
|
|
this.spawnPoints.push({
|
|
x: farmCenter.x + Math.cos(rad) * spawnDistance,
|
|
y: farmCenter.y + Math.sin(rad) * spawnDistance,
|
|
angle: angle
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize defense rewards
|
|
*/
|
|
initializeRewards() {
|
|
this.defenseRewards.set(1, { xp: 200, currency: 500, items: ['raider_loot_common'] });
|
|
this.defenseRewards.set(2, { xp: 400, currency: 1000, items: ['raider_loot_common', 'raider_weapon'] });
|
|
this.defenseRewards.set(3, { xp: 800, currency: 2000, items: ['raider_loot_rare', 'raider_armor'] });
|
|
}
|
|
|
|
/**
|
|
* Check if raid should trigger
|
|
*/
|
|
checkRaidTriggers() {
|
|
if (this.isRaidActive) return;
|
|
|
|
const now = Date.now();
|
|
const timeSinceLastRaid = now - this.lastRaidTime;
|
|
|
|
// Cooldown check
|
|
if (timeSinceLastRaid < this.timeBetweenRaids) return;
|
|
|
|
const fame = this.scene.gameState?.fame || 0;
|
|
const totalResources = this.scene.inventorySystem?.getTotalResourceValue() || 0;
|
|
|
|
// Trigger conditions
|
|
if (fame >= this.fameThreshold || totalResources >= this.resourceThreshold) {
|
|
this.startRaid();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start a raid
|
|
*/
|
|
startRaid(difficulty = null) {
|
|
if (this.isRaidActive) return;
|
|
|
|
this.isRaidActive = true;
|
|
this.currentWave = 0;
|
|
this.raidDifficulty = difficulty || this.calculateDifficulty();
|
|
this.totalWaves = 2 + this.raidDifficulty; // 3-5 waves
|
|
this.lastRaidTime = Date.now();
|
|
|
|
// Notification
|
|
this.scene.uiSystem?.showNotification(
|
|
`RAID INCOMING! ${this.totalWaves} waves approaching!`,
|
|
'danger',
|
|
{ priority: 'high' }
|
|
);
|
|
|
|
// SFX: Raid horn
|
|
this.scene.soundSystem?.play('raid_horn');
|
|
|
|
// VFX: Red screen flash
|
|
this.scene.cameras.main.flash(1000, 255, 0, 0);
|
|
|
|
// Start first wave
|
|
this.scene.time.delayedCall(3000, () => this.spawnWave());
|
|
|
|
console.log(`🚨 RAID STARTED: Difficulty ${this.raidDifficulty}, ${this.totalWaves} waves`);
|
|
}
|
|
|
|
/**
|
|
* Calculate raid difficulty based on game progress
|
|
*/
|
|
calculateDifficulty() {
|
|
const fame = this.scene.gameState?.fame || 0;
|
|
const population = this.scene.gameState?.population || 0;
|
|
|
|
// Scale difficulty
|
|
return Math.min(5, Math.floor((fame / 500) + (population / 20)));
|
|
}
|
|
|
|
/**
|
|
* Spawn a wave of raiders
|
|
*/
|
|
spawnWave() {
|
|
this.currentWave++;
|
|
|
|
const raiderCount = 3 + (this.raidDifficulty * 2); // 5-13 raiders
|
|
const waveRaiders = [];
|
|
|
|
// Spawn raiders
|
|
for (let i = 0; i < raiderCount; i++) {
|
|
const spawnPoint = Phaser.Utils.Array.GetRandom(this.spawnPoints);
|
|
const raider = this.spawnRaider(spawnPoint.x, spawnPoint.y);
|
|
waveRaiders.push(raider);
|
|
}
|
|
|
|
this.activeRaiders.push(...waveRaiders);
|
|
|
|
// Notification
|
|
this.scene.uiSystem?.showNotification(
|
|
`Wave ${this.currentWave}/${this.totalWaves}: ${raiderCount} raiders!`,
|
|
'warning'
|
|
);
|
|
|
|
console.log(`🔥 Wave ${this.currentWave}: Spawned ${raiderCount} raiders`);
|
|
|
|
// Check for next wave
|
|
this.scene.time.delayedCall(5000, () => this.checkWaveProgress());
|
|
}
|
|
|
|
/**
|
|
* Spawn individual raider
|
|
*/
|
|
spawnRaider(x, y) {
|
|
// Create raider sprite
|
|
const raider = this.scene.physics.add.sprite(x, y, 'nomad_raider');
|
|
|
|
// Stats based on difficulty
|
|
raider.maxHealth = 50 + (this.raidDifficulty * 20);
|
|
raider.health = raider.maxHealth;
|
|
raider.attackDamage = 10 + (this.raidDifficulty * 5);
|
|
raider.active = true;
|
|
|
|
// Add AI
|
|
raider.ai = new NomadRaiderAI(this.scene, raider);
|
|
|
|
// Collisions
|
|
this.scene.physics.add.collider(raider, this.scene.player);
|
|
this.scene.physics.add.collider(raider, this.scene.zombieScout);
|
|
|
|
// Health bar
|
|
raider.healthBar = this.scene.add.graphics();
|
|
|
|
// Death handler
|
|
raider.takeDamage = (amount) => this.raiderTakeDamage(raider, amount);
|
|
|
|
return raider;
|
|
}
|
|
|
|
/**
|
|
* Raider takes damage
|
|
*/
|
|
raiderTakeDamage(raider, amount) {
|
|
raider.health -= amount;
|
|
|
|
// Update health bar
|
|
this.updateRaiderHealthBar(raider);
|
|
|
|
// VFX
|
|
raider.setTint(0xff0000);
|
|
this.scene.time.delayedCall(100, () => raider.clearTint());
|
|
|
|
// Death
|
|
if (raider.health <= 0) {
|
|
this.raiderDeath(raider);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Raider death
|
|
*/
|
|
raiderDeath(raider) {
|
|
raider.active = false;
|
|
|
|
// Drop loot
|
|
this.dropRaiderLoot(raider.x, raider.y);
|
|
|
|
// VFX
|
|
this.scene.vfxSystem?.playEffect('raider_death', raider.x, raider.y);
|
|
|
|
// Remove from active list
|
|
const index = this.activeRaiders.indexOf(raider);
|
|
if (index > -1) {
|
|
this.activeRaiders.splice(index, 1);
|
|
}
|
|
|
|
// Cleanup
|
|
raider.ai?.destroy();
|
|
raider.healthBar?.destroy();
|
|
raider.destroy();
|
|
|
|
// Check if wave complete
|
|
this.checkWaveProgress();
|
|
}
|
|
|
|
/**
|
|
* Update raider health bar
|
|
*/
|
|
updateRaiderHealthBar(raider) {
|
|
if (!raider.healthBar) return;
|
|
|
|
raider.healthBar.clear();
|
|
|
|
const barWidth = 40;
|
|
const barHeight = 4;
|
|
const healthPercent = raider.health / raider.maxHealth;
|
|
|
|
// Background
|
|
raider.healthBar.fillStyle(0x000000);
|
|
raider.healthBar.fillRect(raider.x - barWidth / 2, raider.y - 30, barWidth, barHeight);
|
|
|
|
// Health
|
|
raider.healthBar.fillStyle(0xff0000);
|
|
raider.healthBar.fillRect(raider.x - barWidth / 2, raider.y - 30, barWidth * healthPercent, barHeight);
|
|
}
|
|
|
|
/**
|
|
* Drop loot from raider
|
|
*/
|
|
dropRaiderLoot(x, y) {
|
|
const lootTable = ['wood', 'stone', 'metal', 'raider_weapon', 'medicine'];
|
|
const drop = Phaser.Utils.Array.GetRandom(lootTable);
|
|
const amount = Phaser.Math.Between(1, 5);
|
|
|
|
// Create loot drop
|
|
this.scene.lootSystem?.createDrop(x, y, drop, amount);
|
|
}
|
|
|
|
/**
|
|
* Check if wave is complete
|
|
*/
|
|
checkWaveProgress() {
|
|
if (!this.isRaidActive) return;
|
|
|
|
const aliveRaiders = this.activeRaiders.filter(r => r.active);
|
|
|
|
if (aliveRaiders.length === 0) {
|
|
// Wave complete
|
|
if (this.currentWave < this.totalWaves) {
|
|
// Next wave
|
|
this.scene.uiSystem?.showNotification(
|
|
`Wave ${this.currentWave} cleared! Next wave incoming...`,
|
|
'success'
|
|
);
|
|
|
|
this.scene.time.delayedCall(5000, () => this.spawnWave());
|
|
} else {
|
|
// Raid complete
|
|
this.endRaid(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* End raid (success or failure)
|
|
*/
|
|
endRaid(success) {
|
|
this.isRaidActive = false;
|
|
|
|
if (success) {
|
|
// Victory!
|
|
const rewards = this.defenseRewards.get(this.raidDifficulty) || this.defenseRewards.get(1);
|
|
|
|
this.scene.uiSystem?.showNotification(
|
|
`RAID DEFENDED! +${rewards.xp} XP, +${rewards.currency} coins`,
|
|
'victory',
|
|
{ priority: 'high' }
|
|
);
|
|
|
|
// Grant rewards
|
|
this.scene.zombieScoutLeveling?.awardXP(rewards.xp, 'raid_defense');
|
|
this.scene.economySystem?.addCurrency(rewards.currency);
|
|
|
|
rewards.items.forEach(item => {
|
|
this.scene.inventorySystem?.addItem(item, 1);
|
|
});
|
|
|
|
// VFX
|
|
this.scene.vfxSystem?.playEffect('victory', 400, 300);
|
|
this.scene.cameras.main.flash(1000, 0, 255, 0);
|
|
|
|
console.log(`✅ RAID DEFENDED! Rewards granted.`);
|
|
} else {
|
|
// Defeat
|
|
this.scene.uiSystem?.showNotification(
|
|
'RAID DEFEATED YOU! Farm damaged.',
|
|
'defeat',
|
|
{ priority: 'high' }
|
|
);
|
|
|
|
// Damage farm buildings
|
|
this.damageFarm();
|
|
|
|
console.log(`❌ RAID FAILED! Farm damaged.`);
|
|
}
|
|
|
|
// Cleanup remaining raiders
|
|
this.activeRaiders.forEach(raider => {
|
|
raider.ai?.destroy();
|
|
raider.healthBar?.destroy();
|
|
raider.destroy();
|
|
});
|
|
this.activeRaiders = [];
|
|
}
|
|
|
|
/**
|
|
* Damage farm on raid failure
|
|
*/
|
|
damageFarm() {
|
|
// Damage random buildings
|
|
const buildings = this.scene.buildingSystem?.getAllBuildings() || [];
|
|
const damagedCount = Math.min(3, buildings.length);
|
|
|
|
for (let i = 0; i < damagedCount; i++) {
|
|
const building = Phaser.Utils.Array.GetRandom(buildings);
|
|
building.takeDamage?.(50);
|
|
}
|
|
|
|
// Destroy random crops
|
|
const crops = this.scene.crops || [];
|
|
const destroyedCrops = Math.min(10, crops.length);
|
|
|
|
for (let i = 0; i < destroyedCrops; i++) {
|
|
const crop = Phaser.Utils.Array.GetRandom(crops);
|
|
crop.destroy?.();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manual raid trigger (for testing/quests)
|
|
*/
|
|
triggerRaid(difficulty = 1) {
|
|
this.startRaid(difficulty);
|
|
}
|
|
|
|
/**
|
|
* Get raid state
|
|
*/
|
|
getRaidState() {
|
|
return {
|
|
isActive: this.isRaidActive,
|
|
currentWave: this.currentWave,
|
|
totalWaves: this.totalWaves,
|
|
difficulty: this.raidDifficulty,
|
|
activeRaiders: this.activeRaiders.filter(r => r.active).length
|
|
};
|
|
}
|
|
}
|