summaryrefslogtreecommitdiff
path: root/sim/src/BaseBattle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sim/src/BaseBattle.cpp')
-rw-r--r--sim/src/BaseBattle.cpp162
1 files changed, 162 insertions, 0 deletions
diff --git a/sim/src/BaseBattle.cpp b/sim/src/BaseBattle.cpp
new file mode 100644
index 0000000..33e0957
--- /dev/null
+++ b/sim/src/BaseBattle.cpp
@@ -0,0 +1,162 @@
+#include "BaseBattle.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include <entt/entity/registry.hpp>
+#include <entt/signal/dispatcher.hpp>
+
+#include <kurator/battles/components.h>
+#include <kurator/battles/events.h>
+#include <kurator/battles/Scenario.h>
+#include <kurator/universe.h>
+
+#include "Builder.h"
+
+
+namespace kurator
+{
+namespace battles
+{
+
+
+BaseBattle::BaseBattle(const Scenario& scenario) :
+ _registry {},
+ spawner {scenario.total_teams(), 12000, 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<Team, AIState>();
+ 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<Transform, AIState>();
+ for (auto&& [entity, self, ai] : view.each()) {
+ if (!_registry.valid(ai.target))
+ continue;
+ const auto target = _registry.get<Transform>(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<Transform, FloatingMovement, AIState>();
+ 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<TurretControl, universe::TurretType>();
+ for (auto&& [entity, control, def] : view.each()) {
+ if (!_registry.valid(control.owner)) {
+ _registry.destroy(entity);
+ continue;
+ }
+ const auto& [state, transform] = _registry.get<AIState, Transform>(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<HitPoints>(state.target);
+ const auto& target = _registry.get<Transform>(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, entity, state.target});
+ }
+ control.reload = def.rate_of_fire;
+ }
+ }
+}
+
+
+void
+BaseBattle::kill_off_dead()
+{
+ auto view = _registry.view<HitPoints>();
+ for (auto&& [entity, points] : view.each()) {
+ if (points.health <= 0.0)
+ _registry.destroy(entity);
+ }
+}
+
+
+} // namespace battles
+} // namespace kurator