feat: extend Raid
This commit is contained in:
1 parent
42e57d1323
commit
71a936867b
1 file changed
+144
@@ -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
|
||||
Reference in new issue
Block a user