SYSTEMS 4-6/9 COMPLETE: ZombieScoutSkills (skill tree, active/passive abilities), NomadRaiderAI (state machine, pathfinding, loot stealing), FarmRaidSystem (wave spawning, difficulty scaling, rewards). Progress: 6/9 systems (67%).
This commit is contained in:
392
src/systems/FarmRaidSystem.js
Normal file
392
src/systems/FarmRaidSystem.js
Normal file
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user