#include "BaseBattle.h" #include #include #include #include #include #include #include #include #include #include "Builder.h" namespace kurator { namespace sim { BaseBattle::BaseBattle(const campaign::Scenario& scenario) : time {0.0}, _registry {}, spawner {scenario.last_team(), scenario.radius, 0.1} { const auto repo = universe::load_sample(); Builder build {_registry, spawner}; for (const auto& ship : scenario.ships) { const auto entity = build(repo->ship_type(ship.type), ship.team); _registry.emplace(entity, ship.identifier); for (const auto& turret_type : ship.turrets) build(repo->turret_type(turret_type), entity); manager.add(ship.team, entity); // registry supports on construction events } } entt::registry& BaseBattle::registry() { return _registry; } entt::dispatcher& BaseBattle::dispatcher() { return _dispatcher; } void BaseBattle::update(const float dt) { time += dt; pick_random_targets(); keep_at_range(); FloatingMovement::update(_registry, _dispatcher, dt); turrets(dt); kill_off_dead(); manager.clear(_registry); // registry supports on destructions events manager.update(_dispatcher); } void BaseBattle::pick_random_targets() { auto view = _registry.view(); for (auto&& [entity, team, ai] : view.each()) { if (!_registry.valid(ai.target)) ai.target = manager.random(team.id); } } void BaseBattle::keep_at_range() { auto view = _registry.view(); for (auto&& [entity, self, ai] : view.each()) { if (!_registry.valid(ai.target)) continue; const auto target = _registry.get(ai.target); const Point offset = target.position - self.position; ai.destination = target.position - offset.normalized().scale(ai.keep_at_range); } } void BaseBattle::turrets(const float dt) { auto view = _registry.view(); for (auto&& [entity, control, def] : view.each()) { if (!_registry.valid(control.owner)) { _registry.destroy(entity); continue; } const auto& [state, transform] = _registry.get(control.owner); // no checks if (!_registry.valid(state.target)) continue; if (control.reload > 0.0) control.reload -= dt; if (control.rounds < 1 && control.reload <= 0.0) control.rounds = def.rounds; if (control.delay > 0.0) control.delay -= dt; if (control.rounds > 0 && control.delay <= 0.0) { auto& target_points = _registry.get(state.target); const auto& target = _registry.get(state.target); const auto distance = transform.position.distance(target.position); if (distance > def.optimal_range * 2.5) continue; const auto damage = def.effective_damage(distance); if (damage > 0.0) { target_points.health -= damage; _dispatcher.trigger(Hit{damage, control.owner, state.target}); } control.delay = def.rate_of_fire; if (--control.rounds < 1) control.reload = def.reload; } } } void BaseBattle::kill_off_dead() { auto view = _registry.view(); for (auto&& [entity, points] : view.each()) { if (points.health > 0.0) continue; if (_registry.all_of(entity)) { const auto& [identifier, team] = _registry.get(entity); _dispatcher.trigger(stats::ShipLeft{time, identifier, team.id, true}); } _registry.destroy(entity); } } } // namespace sim } // namespace kurator