move ecs related code to own folder

This commit is contained in:
2026-05-25 08:46:58 +02:00
parent 8ad7530740
commit 25ff3c56c5
54 changed files with 877 additions and 680 deletions

View File

@@ -0,0 +1,238 @@
#include "ShipSystem.h"
#include <cassert>
#include <map>
#include <utility>
#include "DynamicBodyComponent.h"
#include "EntityAdmin.h"
#include "FactionComponent.h"
#include "HealthComponent.h"
#include "ModulesConfig.h"
#include "MovementIntentComponent.h"
#include "RallyBehaviorComponent.h"
#include "RepairBehaviorComponent.h"
#include "RepairToolComponent.h"
#include "SalvageBehaviorComponent.h"
#include "SalvageCargoComponent.h"
#include "SensorRangeComponent.h"
#include "Tick.h"
#include "ThreatResponseBehaviorComponent.h"
#include "WeaponComponent.h"
ShipSystem::ShipSystem(const GameConfig& config, EntityAdmin& admin)
: m_config(config)
, m_admin(admin)
{
}
const ShipDef* ShipSystem::findShipDef(const std::string& schematicId) const
{
for (const ShipDef& def : m_config.ships.ships)
{
if (def.id == schematicId)
{
return &def;
}
}
return nullptr;
}
const ModuleDef* ShipSystem::findModuleDef(const std::string& id) const
{
for (const ModuleDef& def : m_config.modules.modules)
{
if (def.id == id)
{
return &def;
}
}
return nullptr;
}
entt::entity ShipSystem::spawn(const std::string& schematicId, int level,
QVector2D position, bool isEnemy,
const std::optional<ShipLayoutConfig>& layout)
{
const ShipDef* def = findShipDef(schematicId);
assert(def != nullptr);
const double x = static_cast<double>(level);
const float tickRate = static_cast<float>(kTickRateHz);
float hp = static_cast<float>(def->health.hpFormula.evaluate(x));
float maxHp = hp;
float maxSpeedPerTick = static_cast<float>(def->movement.speedFormula.evaluate(x))
/ tickRate;
float mainAccelPerTick = static_cast<float>(
def->movement.mainAccelerationFormula.evaluate(x))
/ tickRate;
float maneuveringAccelPerTick = static_cast<float>(
def->movement.maneuveringAccelerationFormula.evaluate(x))
/ tickRate;
float angularAccelPerTick = static_cast<float>(
def->movement.angularAccelerationFormula.evaluate(x))
/ tickRate;
float maxRotationSpeedPerTick = static_cast<float>(
def->movement.maxRotationSpeedFormula.evaluate(x))
/ tickRate;
float sensorRange = static_cast<float>(
def->sensor.sensorRangeFormula.evaluate(x));
entt::entity entity = m_admin.spawnShip(
position, hp, maxHp,
maxSpeedPerTick, mainAccelPerTick, maneuveringAccelPerTick,
angularAccelPerTick, maxRotationSpeedPerTick, sensorRange,
level, schematicId, isEnemy);
// Optional components based on ship role.
if (def->combat)
{
WeaponComponent w;
w.damage = static_cast<float>(def->combat->damageFormula.evaluate(x));
w.range = static_cast<float>(def->combat->attackRangeFormula.evaluate(x));
w.fireRateHz = static_cast<float>(def->combat->attackRateFormula.evaluate(x));
w.cooldownTicks = 0.0f;
w.currentTarget = std::nullopt;
m_admin.addComponent<WeaponComponent>(entity, w);
m_admin.addComponent<ThreatResponseBehaviorComponent>(
entity, ThreatResponseBehaviorComponent{});
if (!isEnemy)
{
m_admin.addComponent<RallyBehaviorComponent>(
entity, RallyBehaviorComponent{m_rallyPoint});
}
}
if (def->salvage)
{
SalvageCargoComponent cargo;
cargo.capacity = def->salvage->cargoCapacity;
cargo.current = 0;
cargo.collectionRange = static_cast<float>(def->salvage->collectionRange);
m_admin.addComponent<SalvageCargoComponent>(entity, cargo);
SalvageBehaviorComponent salvageBehavior;
salvageBehavior.scrapTarget = std::nullopt;
salvageBehavior.deliveryBay = kInvalidBuildingId;
m_admin.addComponent<SalvageBehaviorComponent>(entity, salvageBehavior);
}
if (def->repair)
{
RepairToolComponent rt;
rt.ratePerTick = static_cast<float>(def->repair->repairRateFormula.evaluate(x));
rt.range = static_cast<float>(def->repair->repairRangeFormula.evaluate(x));
rt.currentTarget = std::nullopt;
m_admin.addComponent<RepairToolComponent>(entity, rt);
m_admin.addComponent<RepairBehaviorComponent>(entity, RepairBehaviorComponent{});
}
// Apply module stat modifiers (REQ-MOD-STAT-CALC).
if (layout.has_value() && !layout->placedModules.empty())
{
std::map<std::string, std::pair<double, double>> mods;
for (const PlacedModule& pm : layout->placedModules)
{
const ModuleDef* modDef = findModuleDef(pm.moduleId);
if (!modDef)
{
continue;
}
for (const ModuleStatModifier& sm : modDef->statModifiers)
{
const double val = sm.formula.evaluate(
static_cast<double>(modDef->playerProductionLevel));
std::pair<double, double>& acc = mods[sm.stat];
if (sm.modifierType == "multiplicative")
{
acc.first += (val - 1.0);
}
else
{
acc.second += val;
}
}
}
auto applyMod = [&mods](float& stat, const std::string& name) {
const std::map<std::string, std::pair<double, double>>::const_iterator it =
mods.find(name);
if (it != mods.end())
{
stat = static_cast<float>(
static_cast<double>(stat) * (1.0 + it->second.first)
+ it->second.second);
}
};
HealthComponent& health = m_admin.get<HealthComponent>(entity);
DynamicBodyComponent& dynamics = m_admin.get<DynamicBodyComponent>(entity);
SensorRangeComponent& sensor = m_admin.get<SensorRangeComponent>(entity);
applyMod(health.maxHp, "hp");
health.hp = health.maxHp;
applyMod(dynamics.maxSpeedPerTick, "speed");
applyMod(dynamics.mainAccelerationPerTick, "main_acceleration");
applyMod(dynamics.maneuveringAccelerationPerTick, "maneuvering_acceleration");
applyMod(dynamics.angularAccelerationPerTick, "angular_acceleration");
applyMod(dynamics.maxRotationSpeedPerTick, "max_rotation_speed");
applyMod(sensor.value, "sensor_range");
if (m_admin.hasAll<WeaponComponent>(entity))
{
WeaponComponent& weapon = m_admin.get<WeaponComponent>(entity);
applyMod(weapon.damage, "damage");
applyMod(weapon.range, "attack_range");
applyMod(weapon.fireRateHz, "attack_rate");
}
if (m_admin.hasAll<RepairToolComponent>(entity))
{
RepairToolComponent& repairTool = m_admin.get<RepairToolComponent>(entity);
applyMod(repairTool.ratePerTick, "repair_rate");
applyMod(repairTool.range, "repair_range");
}
}
return entity;
}
void ShipSystem::despawn(entt::entity entity)
{
m_admin.destroy(entity);
}
void ShipSystem::clearMovementIntents()
{
m_admin.forEach<MovementIntentComponent>(
[](entt::entity /*e*/, MovementIntentComponent& i)
{
i = MovementIntentComponent{0, QVector2D(0.0f, 0.0f)};
});
}
void ShipSystem::setRallyPoint(QVector2D point)
{
m_rallyPoint = point;
}
void ShipSystem::triggerRallyDeparture()
{
std::vector<entt::entity> toRemove;
m_admin.forEach<RallyBehaviorComponent, FactionComponent>(
[&toRemove](entt::entity e, const RallyBehaviorComponent& /*rb*/,
const FactionComponent& f)
{
if (!f.isEnemy)
{
toRemove.push_back(e);
}
});
for (entt::entity e : toRemove)
{
m_admin.removeComponent<RallyBehaviorComponent>(e);
}
}