move ecs related code to own folder
This commit is contained in:
238
src/lib/ecs/system/ShipSystem.cpp
Normal file
238
src/lib/ecs/system/ShipSystem.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user