feat: extend Raid

This commit is contained in:
dualfroz committed 2026-09-08 21:15:32 +02:00
1 parent 42e57d1323
commit 71a936867b
1 file changed
+144
+144
View File
@@ -320,3 +320,147 @@ void RaidManager::moveRaiders(const std::vector<Villager>& villagers) {
}
}
void RaidManager::resolveRaiderAttacks(std::vector<Villager>& villagers, PoiRegistry& pois) {
Vec3 centrePoint = cellCentre(centerStand);
for (Raider& attacker : attackers) {
if (!attacker.alive) continue;
if (attacker.attackCooldown > 0) {
--attacker.attackCooldown;
continue;
}
const Raider::Stats& stats = raiderStats(attacker.kind);
int victim = -1;
float bestDistance = 0.0f;
for (std::size_t i = 0; i < villagers.size(); ++i) {
if (!villagers[i].alive) continue;
float distance = horizontalDistance(attacker.position, villagers[i].body.position);
if (distance > stats.attackRange) continue;
if (victim < 0 || distance < bestDistance) {
victim = static_cast<int>(i);
bestDistance = distance;
}
}
if (victim >= 0) {
if (damageVillager(villagers[victim], pois, stats.damage)) ++lostVillagers;
attacker.attackCooldown = stats.attackIntervalTicks;
continue;
}
if (spawner.golemsAlive > 0 &&
horizontalDistance(attacker.position, centrePoint) <= raidGolemEngageRadius) {
for (std::size_t i = 0; i < golemHealth.size(); ++i) {
if (golemHealth[i] <= 0.0f) continue;
golemHealth[i] -= stats.damage;
if (golemHealth[i] <= 0.0f) {
golemHealth[i] = 0.0f;
--spawner.golemsAlive;
}
break;
}
attacker.attackCooldown = stats.attackIntervalTicks;
}
}
}
void RaidManager::resolveGolems(int livingVillagers) {
if (tickGolemSpawner(spawner, livingVillagers, 1)) golemHealth.push_back(spawner.maxHealth);
if (spawner.golemsAlive <= 0) return;
if (spawner.attackCooldown > 0) {
--spawner.attackCooldown;
return;
}
spawner.attackCooldown = spawner.attackIntervalTicks;
Vec3 centrePoint = cellCentre(centerStand);
for (float health : golemHealth) {
if (health <= 0.0f) continue;
int target = -1;
float bestDistance = 0.0f;
for (std::size_t i = 0; i < attackers.size(); ++i) {
if (!attackers[i].alive) continue;
float distance = horizontalDistance(attackers[i].position, centrePoint);
if (target < 0 || distance < bestDistance) {
target = static_cast<int>(i);
bestDistance = distance;
}
}
if (target < 0) return;
damageRaider(target, spawner.damagePerAttack);
}
}
void RaidManager::finishVictory(std::vector<Villager>& villagers, OmenState& omen) {
raidState = RaidState::Victory;
for (Villager& villager : villagers) {
if (!villager.alive) continue;
villager.gossip.add(GossipKind::MajorPositive, gossipMajorPositiveGain);
}
grantHeroOfTheVillage(omen, omenLevel, heroDurationTicks);
}
void RaidManager::stepOnce(const World& world, std::vector<Villager>& villagers, PoiRegistry& pois,
OmenState& omen, const VillagerSchedule& schedule,
const TimeOfDay& clock, const PathConfig& config) {
++elapsed;
for (Villager& villager : villagers)
tickVillager(world, villager, pois, clock, schedule, villageCenter, true, raidTickSeconds,
config);
if (raidState == RaidState::Spawning) {
spawnWave(world, config);
raidState = RaidState::Fighting;
waveTicks = 0;
clearedTicks = 0;
}
if (raidState != RaidState::Fighting) return;
moveRaiders(villagers);
resolveRaiderAttacks(villagers, pois);
int livingVillagers = 0;
for (const Villager& villager : villagers)
if (villager.alive) ++livingVillagers;
resolveGolems(livingVillagers);
++waveTicks;
if (livingVillagers == 0) {
raidState = RaidState::Defeat;
return;
}
int living = livingRaiders();
if (living == 0) {
++clearedTicks;
if (waveIdx + 1 >= waves) {
finishVictory(villagers, omen);
return;
}
if (clearedTicks >= raidWaveDelayTicks) {
++waveIdx;
raidState = RaidState::Spawning;
}
return;
}
if (waveTicks >= raidWaveTimeoutTicks && waveIdx + 1 < waves) {
++waveIdx;
raidState = RaidState::Spawning;
}
}
void RaidManager::tick(const World& world, std::vector<Villager>& villagers, PoiRegistry& pois,
OmenState& omen, const VillagerSchedule& schedule, const TimeOfDay& clock,
int ticks, const PathConfig& config) {
if (!active()) return;
standableNear(world, villageCenter, 8, config, centerStand);
for (int i = 0; i < ticks && active(); ++i)
stepOnce(world, villagers, pois, omen, schedule, clock, config);
}
} // namespace vf