#include "BaseBattle.h" #include #include #include #include #include #include #include #include #include "Builder.h" namespace kurator { namespace sim { BaseBattle::BaseBattle(const campaign::Scenario& scenario) : _registry {}, spawner {scenario.total_teams(), 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); 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) { pick_random_targets(); keep_at_range(); floating_movement(dt); turrets(dt); kill_off_dead(); manager.clear(_registry); // registry supports on destructions events } 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 + 1) % 2); // FIXME } } 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(6000.0); } } void BaseBattle::floating_movement(const float dt) { auto view = _registry.view(); for (auto&& [entity, transform, movement, ai] : view.each()) { const auto offset = ai.destination - transform.position; const auto at_destination = offset.magnitude() > movement.destination_boundary; const auto acceleration = at_destination ? offset.normalized().scale(movement.acceleration * dt) : offset.normalized().scale(-1 * movement.deceleration * dt); movement.speed.x += acceleration.x; movement.speed.y += acceleration.y; if (movement.speed.magnitude() > movement.max_speed) movement.speed = movement.speed.normalized().scale(movement.max_speed); const auto speed = movement.speed.scale(dt); transform.position.x += speed.x; transform.position.y += speed.y; transform.angle = speed.angle(); } } double effective_damage(const universe::TurretType& def, const double distance) { const auto overflow = distance - def.optimal_range; const auto falloff = std::max(0.0, overflow / def.optimal_range / def.falloff_modifier); return def.base_damage * std::round(std::pow(def.falloff_intensity, std::pow(falloff, 2)) * 1000) / 1000; } 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.reload <= 0.0) { auto& target_points = _registry.get(state.target); const auto& target = _registry.get(state.target); const auto distance = transform.position - target.position; const auto damage = effective_damage(def, distance.magnitude()); if (damage > 0.0) { target_points.health -= damage; _dispatcher.trigger(Hit{damage, control.owner, state.target}); } control.reload = def.rate_of_fire; } } } void BaseBattle::kill_off_dead() { auto view = _registry.view(); for (auto&& [entity, points] : view.each()) { if (points.health <= 0.0) _registry.destroy(entity); } } } // namespace sim } // namespace kurator