move ecs related code to own folder
This commit is contained in:
@@ -12,14 +12,25 @@
|
||||
#include "ConfigLoader.h"
|
||||
#include "DynamicBodyComponent.h"
|
||||
#include "DynamicBodySystem.h"
|
||||
#include "EcsComponents.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "FactionComponent.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "HomeReturnBehaviorComponent.h"
|
||||
#include "MovementIntentComponent.h"
|
||||
#include "MovementIntentSystem.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "RallyBehaviorComponent.h"
|
||||
#include "RepairBehaviorComponent.h"
|
||||
#include "RepairToolComponent.h"
|
||||
#include "Rotation.h"
|
||||
#include "SalvageBehaviorComponent.h"
|
||||
#include "SalvageCargoComponent.h"
|
||||
#include "ScrapSystem.h"
|
||||
#include "Ship.h"
|
||||
#include "SensorRangeComponent.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "Tick.h"
|
||||
#include "ThreatResponseBehaviorComponent.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixture
|
||||
@@ -78,19 +89,19 @@ struct Fixture
|
||||
};
|
||||
|
||||
// Helpers to read ECS data for a ship entity.
|
||||
static const MovementIntent& intent(EntityAdmin& a, entt::entity e)
|
||||
static const MovementIntentComponent& intent(EntityAdmin& a, entt::entity e)
|
||||
{
|
||||
return a.get<MovementIntent>(e);
|
||||
return a.get<MovementIntentComponent>(e);
|
||||
}
|
||||
|
||||
static const Health& health(EntityAdmin& a, entt::entity e)
|
||||
static const HealthComponent& health(EntityAdmin& a, entt::entity e)
|
||||
{
|
||||
return a.get<Health>(e);
|
||||
return a.get<HealthComponent>(e);
|
||||
}
|
||||
|
||||
static const Position& pos(EntityAdmin& a, entt::entity e)
|
||||
static const PositionComponent& pos(EntityAdmin& a, entt::entity e)
|
||||
{
|
||||
return a.get<Position>(e);
|
||||
return a.get<PositionComponent>(e);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -103,7 +114,7 @@ TEST_CASE("BehaviorSystem: clearMovementIntents resets all ships to priority 0",
|
||||
Fixture f;
|
||||
const entt::entity e = f.ships.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
f.admin.get<MovementIntent>(e) = MovementIntent{3, QVector2D(10.0f, 0.0f)};
|
||||
f.admin.get<MovementIntentComponent>(e) = MovementIntentComponent{3, QVector2D(10.0f, 0.0f)};
|
||||
f.ships.clearMovementIntents();
|
||||
|
||||
REQUIRE(intent(f.admin, e).priority == 0);
|
||||
@@ -120,7 +131,7 @@ TEST_CASE("BehaviorSystem: tickMovement advances ship by maxSpeedPerTick toward
|
||||
const entt::entity e = f.ships.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
const float speed = f.admin.get<DynamicBodyComponent>(e).maxSpeedPerTick;
|
||||
f.admin.get<MovementIntent>(e) = MovementIntent{1, QVector2D(100.0f, 0.0f)};
|
||||
f.admin.get<MovementIntentComponent>(e) = MovementIntentComponent{1, QVector2D(100.0f, 0.0f)};
|
||||
f.movementIntent.tick(f.admin);
|
||||
f.dynamicBody.tick(f.admin);
|
||||
|
||||
@@ -136,7 +147,7 @@ TEST_CASE("BehaviorSystem: tickMovement stops exactly at target without overshoo
|
||||
|
||||
const float speed = f.admin.get<DynamicBodyComponent>(e).maxSpeedPerTick;
|
||||
const QVector2D target(speed * 0.5f, 0.0f);
|
||||
f.admin.get<MovementIntent>(e) = MovementIntent{1, target};
|
||||
f.admin.get<MovementIntentComponent>(e) = MovementIntentComponent{1, target};
|
||||
f.movementIntent.tick(f.admin);
|
||||
f.dynamicBody.tick(f.admin);
|
||||
|
||||
@@ -153,8 +164,8 @@ TEST_CASE("BehaviorSystem: tickHomeReturnBehavior does nothing when HP is above
|
||||
{
|
||||
Fixture f;
|
||||
const entt::entity e = f.ships.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
f.admin.addComponent<HomeReturnBehavior>(e, HomeReturnBehavior{0.3f, QVector2D(-10.0f, 0.0f)});
|
||||
f.admin.get<Health>(e).hp = f.admin.get<Health>(e).maxHp; // full HP
|
||||
f.admin.addComponent<HomeReturnBehaviorComponent>(e, HomeReturnBehaviorComponent{0.3f, QVector2D(-10.0f, 0.0f)});
|
||||
f.admin.get<HealthComponent>(e).hp = f.admin.get<HealthComponent>(e).maxHp; // full HP
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickHomeReturnBehavior(f.admin);
|
||||
@@ -168,8 +179,8 @@ TEST_CASE("BehaviorSystem: tickHomeReturnBehavior writes priority-4 intent towar
|
||||
Fixture f;
|
||||
const entt::entity e = f.ships.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
const QVector2D homePos(-10.0f, 0.0f);
|
||||
f.admin.addComponent<HomeReturnBehavior>(e, HomeReturnBehavior{0.5f, homePos});
|
||||
f.admin.get<Health>(e).hp = f.admin.get<Health>(e).maxHp * 0.2f; // below threshold
|
||||
f.admin.addComponent<HomeReturnBehaviorComponent>(e, HomeReturnBehaviorComponent{0.5f, homePos});
|
||||
f.admin.get<HealthComponent>(e).hp = f.admin.get<HealthComponent>(e).maxHp * 0.2f; // below threshold
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickHomeReturnBehavior(f.admin);
|
||||
@@ -186,8 +197,8 @@ TEST_CASE("BehaviorSystem: tickHomeReturnBehavior priority-4 beats tickThreatRes
|
||||
f.ships.spawn("interceptor", 1, QVector2D(5.0f, 0.0f), /*isEnemy=*/true);
|
||||
|
||||
const QVector2D homePos(-50.0f, 0.0f);
|
||||
f.admin.addComponent<HomeReturnBehavior>(player, HomeReturnBehavior{0.5f, homePos});
|
||||
f.admin.get<Health>(player).hp = f.admin.get<Health>(player).maxHp * 0.1f;
|
||||
f.admin.addComponent<HomeReturnBehaviorComponent>(player, HomeReturnBehaviorComponent{0.5f, homePos});
|
||||
f.admin.get<HealthComponent>(player).hp = f.admin.get<HealthComponent>(player).maxHp * 0.1f;
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickHomeReturnBehavior(f.admin);
|
||||
@@ -212,8 +223,8 @@ TEST_CASE("BehaviorSystem: player combat ship acquires nearest enemy ship in ran
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.hasAll<ThreatResponseBehavior>(player));
|
||||
const ThreatResponseBehavior& threatResponseBehavior = f.admin.get<ThreatResponseBehavior>(player);
|
||||
REQUIRE(f.admin.hasAll<ThreatResponseBehaviorComponent>(player));
|
||||
const ThreatResponseBehaviorComponent& threatResponseBehavior = f.admin.get<ThreatResponseBehaviorComponent>(player);
|
||||
REQUIRE(threatResponseBehavior.currentTarget.has_value());
|
||||
REQUIRE(*threatResponseBehavior.currentTarget == enemy);
|
||||
}
|
||||
@@ -228,8 +239,8 @@ TEST_CASE("BehaviorSystem: player combat ship does not target friendly ships",
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.hasAll<ThreatResponseBehavior>(e1));
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehavior>(e1).currentTarget.has_value());
|
||||
REQUIRE(f.admin.hasAll<ThreatResponseBehaviorComponent>(e1));
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehaviorComponent>(e1).currentTarget.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("BehaviorSystem: player combat ship ignores enemy beyond engagement range",
|
||||
@@ -242,7 +253,7 @@ TEST_CASE("BehaviorSystem: player combat ship ignores enemy beyond engagement ra
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehavior>(player).currentTarget.has_value());
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehaviorComponent>(player).currentTarget.has_value());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -260,8 +271,8 @@ TEST_CASE("BehaviorSystem: enemy ship acquires nearest player ship in range",
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.hasAll<ThreatResponseBehavior>(enemy));
|
||||
const ThreatResponseBehavior& threatResponseBehavior = f.admin.get<ThreatResponseBehavior>(enemy);
|
||||
REQUIRE(f.admin.hasAll<ThreatResponseBehaviorComponent>(enemy));
|
||||
const ThreatResponseBehaviorComponent& threatResponseBehavior = f.admin.get<ThreatResponseBehaviorComponent>(enemy);
|
||||
REQUIRE(threatResponseBehavior.currentTarget.has_value());
|
||||
REQUIRE(*threatResponseBehavior.currentTarget == player);
|
||||
}
|
||||
@@ -291,7 +302,7 @@ TEST_CASE("BehaviorSystem: repair ship writes intent toward damaged friendly shi
|
||||
const entt::entity repairShip = f.ships.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
const entt::entity friendly = f.ships.spawn("interceptor", 1, QVector2D(5.0f, 0.0f));
|
||||
|
||||
f.admin.get<Health>(friendly).hp = f.admin.get<Health>(friendly).maxHp * 0.5f;
|
||||
f.admin.get<HealthComponent>(friendly).hp = f.admin.get<HealthComponent>(friendly).maxHp * 0.5f;
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickRepairBehavior(f.admin, f.buildings);
|
||||
@@ -307,8 +318,8 @@ TEST_CASE("BehaviorSystem: repair ship heals damaged ally within repair range",
|
||||
const entt::entity repairShip = f.ships.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
const entt::entity friendly = f.ships.spawn("interceptor", 1, QVector2D(1.0f, 0.0f));
|
||||
|
||||
const float initialHp = f.admin.get<Health>(friendly).maxHp * 0.5f;
|
||||
f.admin.get<Health>(friendly).hp = initialHp;
|
||||
const float initialHp = f.admin.get<HealthComponent>(friendly).maxHp * 0.5f;
|
||||
f.admin.get<HealthComponent>(friendly).hp = initialHp;
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickRepairBehavior(f.admin, f.buildings);
|
||||
@@ -322,7 +333,7 @@ TEST_CASE("BehaviorSystem: repair ship does not heal above maxHp", "[behavior]")
|
||||
f.ships.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
const entt::entity friendly = f.ships.spawn("interceptor", 1, QVector2D(1.0f, 0.0f));
|
||||
|
||||
f.admin.get<Health>(friendly).hp = f.admin.get<Health>(friendly).maxHp - 0.001f;
|
||||
f.admin.get<HealthComponent>(friendly).hp = f.admin.get<HealthComponent>(friendly).maxHp - 0.001f;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
@@ -330,7 +341,7 @@ TEST_CASE("BehaviorSystem: repair ship does not heal above maxHp", "[behavior]")
|
||||
f.ai.tickRepairBehavior(f.admin, f.buildings);
|
||||
}
|
||||
|
||||
const Health& h = health(f.admin, friendly);
|
||||
const HealthComponent& h = health(f.admin, friendly);
|
||||
REQUIRE(h.hp <= h.maxHp);
|
||||
REQUIRE(h.hp == Approx(h.maxHp));
|
||||
}
|
||||
@@ -363,7 +374,7 @@ TEST_CASE("BehaviorSystem: salvage ship collects scrap on arrival", "[behavior]"
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.get<SalvageCargo>(ship).current == 1);
|
||||
REQUIRE(f.admin.get<SalvageCargoComponent>(ship).current == 1);
|
||||
REQUIRE_FALSE(f.admin.isValid(scrapEntity));
|
||||
}
|
||||
|
||||
@@ -385,13 +396,13 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
|
||||
REQUIRE(f.buildings.findBuilding(bayId) != nullptr);
|
||||
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(5.0f, 0.0f));
|
||||
SalvageCargo& cargo = f.admin.get<SalvageCargo>(ship);
|
||||
SalvageCargoComponent& cargo = f.admin.get<SalvageCargoComponent>(ship);
|
||||
cargo.current = cargo.capacity; // full cargo
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
const MovementIntent& i = intent(f.admin, ship);
|
||||
const MovementIntentComponent& i = intent(f.admin, ship);
|
||||
REQUIRE(i.priority == 1);
|
||||
REQUIRE(i.target.x() < pos(f.admin, ship).value.x());
|
||||
}
|
||||
@@ -404,7 +415,7 @@ TEST_CASE("SensorRange: sensorRange is populated from config formula at spawn",
|
||||
{
|
||||
Fixture f;
|
||||
const entt::entity e = f.ships.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
REQUIRE(f.admin.get<SensorRange>(e).value == Approx(200.0f));
|
||||
REQUIRE(f.admin.get<SensorRangeComponent>(e).value == Approx(200.0f));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -421,7 +432,7 @@ TEST_CASE("SensorRange: player combat ship acquires enemy just inside sensor ran
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.get<ThreatResponseBehavior>(player).currentTarget == enemy);
|
||||
REQUIRE(f.admin.get<ThreatResponseBehaviorComponent>(player).currentTarget == enemy);
|
||||
}
|
||||
|
||||
TEST_CASE("SensorRange: player combat ship ignores enemy just outside sensor range", "[sensor]")
|
||||
@@ -433,7 +444,7 @@ TEST_CASE("SensorRange: player combat ship ignores enemy just outside sensor ran
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehavior>(player).currentTarget.has_value());
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehaviorComponent>(player).currentTarget.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("SensorRange: enemy ship ignores player just outside sensor range", "[sensor]")
|
||||
@@ -446,7 +457,7 @@ TEST_CASE("SensorRange: enemy ship ignores player just outside sensor range", "[
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickThreatResponseBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehavior>(enemy).currentTarget.has_value());
|
||||
REQUIRE_FALSE(f.admin.get<ThreatResponseBehaviorComponent>(enemy).currentTarget.has_value());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -483,12 +494,12 @@ TEST_CASE("SensorRange: repair ship does not acquire damaged ally beyond sensor
|
||||
Fixture f;
|
||||
const entt::entity repairShip = f.ships.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
const entt::entity friendly = f.ships.spawn("interceptor", 1, QVector2D(300.0f, 0.0f));
|
||||
f.admin.get<Health>(friendly).hp = f.admin.get<Health>(friendly).maxHp * 0.5f;
|
||||
f.admin.get<HealthComponent>(friendly).hp = f.admin.get<HealthComponent>(friendly).maxHp * 0.5f;
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickRepairBehavior(f.admin, f.buildings);
|
||||
|
||||
REQUIRE_FALSE(f.admin.get<RepairBehavior>(repairShip).currentTarget.has_value());
|
||||
REQUIRE_FALSE(f.admin.get<RepairBehaviorComponent>(repairShip).currentTarget.has_value());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -504,6 +515,6 @@ TEST_CASE("SensorRange: salvage ship ignores scrap beyond sensor range", "[senso
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE_FALSE(f.admin.get<SalvageBehavior>(ship).scrapTarget.has_value());
|
||||
REQUIRE_FALSE(f.admin.get<SalvageBehaviorComponent>(ship).scrapTarget.has_value());
|
||||
REQUIRE(intent(f.admin, ship).target.x() > pos(f.admin, ship).value.x());
|
||||
}
|
||||
|
||||
@@ -8,14 +8,18 @@
|
||||
#include "BuildingType.h"
|
||||
#include "CombatSystem.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "EcsComponents.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "FactionComponent.h"
|
||||
#include "FireEvent.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "HqProxyComponent.h"
|
||||
#include "ScrapSystem.h"
|
||||
#include "Ship.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "Simulation.h"
|
||||
#include "StationBodyComponent.h"
|
||||
#include "Tick.h"
|
||||
#include "ThreatResponseBehaviorComponent.h"
|
||||
#include "WeaponComponent.h"
|
||||
|
||||
static GameConfig loadConfig()
|
||||
{
|
||||
@@ -63,14 +67,14 @@ struct CombatFixture
|
||||
|
||||
void wireEnemyTarget(entt::entity enemy, entt::entity playerTarget)
|
||||
{
|
||||
if (admin.hasAll<Weapon>(enemy))
|
||||
if (admin.hasAll<WeaponComponent>(enemy))
|
||||
{
|
||||
admin.get<Weapon>(enemy).currentTarget = playerTarget;
|
||||
admin.get<Weapon>(enemy).cooldownTicks = 0.0f;
|
||||
admin.get<WeaponComponent>(enemy).currentTarget = playerTarget;
|
||||
admin.get<WeaponComponent>(enemy).cooldownTicks = 0.0f;
|
||||
}
|
||||
if (admin.hasAll<ThreatResponseBehavior>(enemy))
|
||||
if (admin.hasAll<ThreatResponseBehaviorComponent>(enemy))
|
||||
{
|
||||
admin.get<ThreatResponseBehavior>(enemy).currentTarget = playerTarget;
|
||||
admin.get<ThreatResponseBehaviorComponent>(enemy).currentTarget = playerTarget;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -89,13 +93,13 @@ TEST_CASE("CombatSystem: ship fires when cooldown=0 and target in range", "[comb
|
||||
const entt::entity player = f.ships.spawn(combatDef->id, 1, QVector2D(4.0f, 5.0f), false);
|
||||
f.wireEnemyTarget(enemy, player);
|
||||
|
||||
const float hpBefore = f.admin.get<Health>(player).hp;
|
||||
const float hpBefore = f.admin.get<HealthComponent>(player).hp;
|
||||
|
||||
std::vector<FireEvent> events;
|
||||
f.combat.tick(0, f.admin, f.buildings, events);
|
||||
f.combat.applyPendingDamage(5, f.admin);
|
||||
|
||||
REQUIRE(f.admin.get<Health>(player).hp < hpBefore);
|
||||
REQUIRE(f.admin.get<HealthComponent>(player).hp < hpBefore);
|
||||
REQUIRE(events.size() >= 1);
|
||||
}
|
||||
|
||||
@@ -109,7 +113,7 @@ TEST_CASE("CombatSystem: cooldown prevents firing before it expires", "[combat]"
|
||||
const entt::entity player = f.ships.spawn(combatDef->id, 1, QVector2D(4.0f, 5.0f), false);
|
||||
|
||||
f.wireEnemyTarget(enemy, player);
|
||||
f.admin.get<Weapon>(enemy).cooldownTicks = 3.0f; // override to 3
|
||||
f.admin.get<WeaponComponent>(enemy).cooldownTicks = 3.0f; // override to 3
|
||||
|
||||
std::vector<FireEvent> events;
|
||||
f.combat.tick(0, f.admin, f.buildings, events);
|
||||
@@ -146,8 +150,8 @@ TEST_CASE("CombatSystem: player station fires at enemy ship in range", "[combat]
|
||||
// Find the player station entity via ECS.
|
||||
entt::entity stationEntity = entt::null;
|
||||
QVector2D stationCenter;
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity e, const StationBody& sb, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity e, const StationBodyComponent& sb, const FactionComponent& f)
|
||||
{
|
||||
if (!f.isEnemy && stationEntity == entt::null)
|
||||
{
|
||||
@@ -184,8 +188,8 @@ TEST_CASE("CombatSystem: enemy station fires at player ship in range", "[combat]
|
||||
|
||||
entt::entity stationEntity = entt::null;
|
||||
QVector2D stationCenter;
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity e, const StationBody& sb, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity e, const StationBodyComponent& sb, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy && stationEntity == entt::null)
|
||||
{
|
||||
@@ -222,8 +226,8 @@ TEST_CASE("CombatSystem: player ship fires at enemy station in range", "[combat]
|
||||
|
||||
entt::entity stationEntity = entt::null;
|
||||
QVector2D stationCenter;
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity e, const StationBody& sb, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity e, const StationBodyComponent& sb, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy && stationEntity == entt::null)
|
||||
{
|
||||
@@ -271,7 +275,7 @@ TEST_CASE("CombatSystem: damage not applied before impact tick", "[combat]")
|
||||
const entt::entity player = f.ships.spawn(combatDef->id, 1, QVector2D(4.0f, 5.0f), false);
|
||||
f.wireEnemyTarget(enemy, player);
|
||||
|
||||
const float hpBefore = f.admin.get<Health>(player).hp;
|
||||
const float hpBefore = f.admin.get<HealthComponent>(player).hp;
|
||||
|
||||
std::vector<FireEvent> events;
|
||||
f.combat.tick(0, f.admin, f.buildings, events);
|
||||
@@ -279,7 +283,7 @@ TEST_CASE("CombatSystem: damage not applied before impact tick", "[combat]")
|
||||
for (Tick t = 1; t < 5; ++t)
|
||||
{
|
||||
f.combat.applyPendingDamage(t, f.admin);
|
||||
REQUIRE(f.admin.get<Health>(player).hp == Approx(hpBefore));
|
||||
REQUIRE(f.admin.get<HealthComponent>(player).hp == Approx(hpBefore));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,13 +297,13 @@ TEST_CASE("CombatSystem: damage applied exactly at impact tick", "[combat]")
|
||||
const entt::entity player = f.ships.spawn(combatDef->id, 1, QVector2D(4.0f, 5.0f), false);
|
||||
f.wireEnemyTarget(enemy, player);
|
||||
|
||||
const float hpBefore = f.admin.get<Health>(player).hp;
|
||||
const float hpBefore = f.admin.get<HealthComponent>(player).hp;
|
||||
|
||||
std::vector<FireEvent> events;
|
||||
f.combat.tick(0, f.admin, f.buildings, events);
|
||||
f.combat.applyPendingDamage(5, f.admin);
|
||||
|
||||
REQUIRE(f.admin.get<Health>(player).hp < hpBefore);
|
||||
REQUIRE(f.admin.get<HealthComponent>(player).hp < hpBefore);
|
||||
}
|
||||
|
||||
TEST_CASE("CombatSystem: damage silently dropped if target already dead", "[combat]")
|
||||
@@ -333,7 +337,7 @@ TEST_CASE("CombatSystem: damage still applied if shooter already dead", "[combat
|
||||
const entt::entity player = f.ships.spawn(combatDef->id, 1, QVector2D(4.0f, 5.0f), false);
|
||||
f.wireEnemyTarget(enemy, player);
|
||||
|
||||
const float hpBefore = f.admin.get<Health>(player).hp;
|
||||
const float hpBefore = f.admin.get<HealthComponent>(player).hp;
|
||||
|
||||
std::vector<FireEvent> events;
|
||||
f.combat.tick(0, f.admin, f.buildings, events);
|
||||
@@ -342,7 +346,7 @@ TEST_CASE("CombatSystem: damage still applied if shooter already dead", "[combat
|
||||
|
||||
f.combat.applyPendingDamage(5, f.admin);
|
||||
|
||||
REQUIRE(f.admin.get<Health>(player).hp < hpBefore);
|
||||
REQUIRE(f.admin.get<HealthComponent>(player).hp < hpBefore);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -359,7 +363,7 @@ TEST_CASE("CombatSystem: dead ship is removed after tick step 9", "[combat]")
|
||||
const entt::entity ship = sim.ships().spawn(combatDef->id, 1,
|
||||
QVector2D(10.0f, 10.0f));
|
||||
|
||||
sim.admin().get<Health>(ship).hp = -1.0f;
|
||||
sim.admin().get<HealthComponent>(ship).hp = -1.0f;
|
||||
|
||||
sim.tick();
|
||||
|
||||
@@ -383,7 +387,7 @@ TEST_CASE("CombatSystem: scrap is spawned on ship death", "[combat]")
|
||||
|
||||
const entt::entity ship = sim.ships().spawn(droppingDef->id, 1,
|
||||
QVector2D(10.0f, 10.0f));
|
||||
sim.admin().get<Health>(ship).hp = -1.0f;
|
||||
sim.admin().get<HealthComponent>(ship).hp = -1.0f;
|
||||
|
||||
sim.tick();
|
||||
|
||||
@@ -395,8 +399,8 @@ TEST_CASE("CombatSystem: HQ death sets game over", "[combat]")
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
// Damage the HQ proxy entity (has HqProxy + Health).
|
||||
sim.admin().forEach<HqProxy, Health>(
|
||||
[](entt::entity /*e*/, const HqProxy& /*hq*/, Health& h)
|
||||
sim.admin().forEach<HqProxyComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const HqProxyComponent& /*hq*/, HealthComponent& h)
|
||||
{
|
||||
h.hp = -1.0f;
|
||||
});
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "DespawnAtComponent.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "ScrapSystem.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -17,8 +19,8 @@ TEST_CASE("ScrapSystem: spawn returns a valid entity with correct scrap data", "
|
||||
const entt::entity e = ss.spawn(QVector2D(3.0f, 4.0f), 5, 100);
|
||||
|
||||
REQUIRE(admin.isValid(e));
|
||||
REQUIRE(admin.get<ScrapData>(e).amount == 5);
|
||||
REQUIRE(admin.get<DespawnAt>(e).tick == 100);
|
||||
REQUIRE(admin.get<ScrapDataComponent>(e).amount == 5);
|
||||
REQUIRE(admin.get<DespawnAtComponent>(e).tick == 100);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
#include "BuildingSystem.h"
|
||||
#include "BuildingType.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "EcsComponents.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "GameConfig.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "ItemType.h"
|
||||
#include "ModulesConfig.h"
|
||||
#include "Rotation.h"
|
||||
#include "Ship.h"
|
||||
#include "SensorRangeComponent.h"
|
||||
#include "ShipLayout.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "Simulation.h"
|
||||
@@ -102,7 +102,7 @@ TEST_CASE("Ship spawn: no modules leaves base stats unchanged", "[modules]")
|
||||
QVector2D(5.0f, 5.0f), false, std::nullopt);
|
||||
|
||||
REQUIRE(sim.admin().isValid(e));
|
||||
CHECK(sim.admin().get<Health>(e).maxHp == Approx(expectedHp));
|
||||
CHECK(sim.admin().get<HealthComponent>(e).maxHp == Approx(expectedHp));
|
||||
}
|
||||
|
||||
TEST_CASE("Ship spawn: multiplicative HP module applies correctly", "[modules]")
|
||||
@@ -128,8 +128,8 @@ TEST_CASE("Ship spawn: multiplicative HP module applies correctly", "[modules]")
|
||||
REQUIRE(sim.admin().isValid(e));
|
||||
// armor_plate has multiplied_hp_formula = "1.5"
|
||||
// final = base * (1 + (1.5 - 1)) + 0 = base * 1.5
|
||||
CHECK(sim.admin().get<Health>(e).maxHp == Approx(baseHp * 1.5f));
|
||||
CHECK(sim.admin().get<Health>(e).hp == sim.admin().get<Health>(e).maxHp);
|
||||
CHECK(sim.admin().get<HealthComponent>(e).maxHp == Approx(baseHp * 1.5f));
|
||||
CHECK(sim.admin().get<HealthComponent>(e).hp == sim.admin().get<HealthComponent>(e).maxHp);
|
||||
}
|
||||
|
||||
TEST_CASE("Ship spawn: additive sensor module applies correctly", "[modules]")
|
||||
@@ -155,7 +155,7 @@ TEST_CASE("Ship spawn: additive sensor module applies correctly", "[modules]")
|
||||
REQUIRE(sim.admin().isValid(e));
|
||||
// sensor_booster has added_sensor_range_formula = "10"
|
||||
// final = base * 1.0 + 10 = base + 10
|
||||
CHECK(sim.admin().get<SensorRange>(e).value == Approx(baseRange + 10.0f));
|
||||
CHECK(sim.admin().get<SensorRangeComponent>(e).value == Approx(baseRange + 10.0f));
|
||||
}
|
||||
|
||||
TEST_CASE("Ship spawn: multiple modules stack correctly", "[modules]")
|
||||
@@ -185,7 +185,7 @@ TEST_CASE("Ship spawn: multiple modules stack correctly", "[modules]")
|
||||
// Two armor_plates: each 1.5 multiplier
|
||||
// total_mult = 1 + (1.5 - 1) + (1.5 - 1) = 2.0
|
||||
// final = base * 2.0
|
||||
CHECK(sim.admin().get<Health>(e).maxHp == Approx(baseHp * 2.0f));
|
||||
CHECK(sim.admin().get<HealthComponent>(e).maxHp == Approx(baseHp * 2.0f));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -5,14 +5,20 @@
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "BuildingId.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "DynamicBodyComponent.h"
|
||||
#include "EcsComponents.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "BuildingId.h"
|
||||
#include "Ship.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "RepairBehaviorComponent.h"
|
||||
#include "RepairToolComponent.h"
|
||||
#include "SalvageBehaviorComponent.h"
|
||||
#include "SalvageCargoComponent.h"
|
||||
#include "SensorRangeComponent.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "Tick.h"
|
||||
#include "ThreatResponseBehaviorComponent.h"
|
||||
#include "WeaponComponent.h"
|
||||
|
||||
static GameConfig loadConfig()
|
||||
{
|
||||
@@ -33,12 +39,12 @@ TEST_CASE("ShipSystem: interceptor spawn has weapon and threatResponse, no cargo
|
||||
const entt::entity e = ss.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
REQUIRE(admin.isValid(e));
|
||||
REQUIRE(admin.hasAll<Weapon>(e));
|
||||
REQUIRE(admin.hasAll<ThreatResponseBehavior>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<SalvageCargo>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<RepairTool>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<RepairBehavior>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<SalvageBehavior>(e));
|
||||
REQUIRE(admin.hasAll<WeaponComponent>(e));
|
||||
REQUIRE(admin.hasAll<ThreatResponseBehaviorComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<SalvageCargoComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<RepairToolComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<RepairBehaviorComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<SalvageBehaviorComponent>(e));
|
||||
}
|
||||
|
||||
TEST_CASE("ShipSystem: interceptor level 1 stats match config formulas", "[ship]")
|
||||
@@ -50,16 +56,16 @@ TEST_CASE("ShipSystem: interceptor level 1 stats match config formulas", "[ship]
|
||||
const entt::entity e = ss.spawn("interceptor", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
// hp_formula = "40 + 5*x" at x=1 → 45
|
||||
REQUIRE(admin.get<Health>(e).maxHp == Approx(45.0f));
|
||||
REQUIRE(admin.get<Health>(e).hp == Approx(45.0f));
|
||||
REQUIRE(admin.get<HealthComponent>(e).maxHp == Approx(45.0f));
|
||||
REQUIRE(admin.get<HealthComponent>(e).hp == Approx(45.0f));
|
||||
// damage_formula = "10 + 2*x" at x=1 → 12
|
||||
REQUIRE(admin.get<Weapon>(e).damage == Approx(12.0f));
|
||||
REQUIRE(admin.get<WeaponComponent>(e).damage == Approx(12.0f));
|
||||
// attack_range_formula = "150"
|
||||
REQUIRE(admin.get<Weapon>(e).range == Approx(150.0f));
|
||||
REQUIRE(admin.get<WeaponComponent>(e).range == Approx(150.0f));
|
||||
// sensor_range_formula = "200"
|
||||
REQUIRE(admin.get<SensorRange>(e).value == Approx(200.0f));
|
||||
REQUIRE(admin.get<SensorRangeComponent>(e).value == Approx(200.0f));
|
||||
// cooldownTicks starts at 0
|
||||
REQUIRE(admin.get<Weapon>(e).cooldownTicks == Approx(0.0f));
|
||||
REQUIRE(admin.get<WeaponComponent>(e).cooldownTicks == Approx(0.0f));
|
||||
}
|
||||
|
||||
TEST_CASE("ShipSystem: interceptor level 5 hp matches formula", "[ship]")
|
||||
@@ -71,7 +77,7 @@ TEST_CASE("ShipSystem: interceptor level 5 hp matches formula", "[ship]")
|
||||
const entt::entity e = ss.spawn("interceptor", 5, QVector2D(0.0f, 0.0f));
|
||||
|
||||
// hp_formula = "40 + 5*x" at x=5 → 65
|
||||
REQUIRE(admin.get<Health>(e).maxHp == Approx(65.0f));
|
||||
REQUIRE(admin.get<HealthComponent>(e).maxHp == Approx(65.0f));
|
||||
}
|
||||
|
||||
TEST_CASE("ShipSystem: interceptor level 0 maxSpeedPerTick matches formula / kTickRateHz", "[ship]")
|
||||
@@ -100,10 +106,10 @@ TEST_CASE("ShipSystem: salvage_ship spawn has cargo and scrapCollector, no weapo
|
||||
|
||||
const entt::entity e = ss.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
REQUIRE(admin.hasAll<SalvageCargo>(e));
|
||||
REQUIRE(admin.hasAll<SalvageBehavior>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<Weapon>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<RepairTool>(e));
|
||||
REQUIRE(admin.hasAll<SalvageCargoComponent>(e));
|
||||
REQUIRE(admin.hasAll<SalvageBehaviorComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<WeaponComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<RepairToolComponent>(e));
|
||||
}
|
||||
|
||||
TEST_CASE("ShipSystem: salvage_ship cargo capacity matches config", "[ship]")
|
||||
@@ -115,10 +121,10 @@ TEST_CASE("ShipSystem: salvage_ship cargo capacity matches config", "[ship]")
|
||||
const entt::entity e = ss.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
// cargo_capacity = 10
|
||||
REQUIRE(admin.get<SalvageCargo>(e).capacity == 10);
|
||||
REQUIRE(admin.get<SalvageCargo>(e).current == 0);
|
||||
REQUIRE(admin.get<SalvageBehavior>(e).deliveryBay == kInvalidBuildingId);
|
||||
REQUIRE_FALSE(admin.get<SalvageBehavior>(e).scrapTarget.has_value());
|
||||
REQUIRE(admin.get<SalvageCargoComponent>(e).capacity == 10);
|
||||
REQUIRE(admin.get<SalvageCargoComponent>(e).current == 0);
|
||||
REQUIRE(admin.get<SalvageBehaviorComponent>(e).deliveryBay == kInvalidBuildingId);
|
||||
REQUIRE_FALSE(admin.get<SalvageBehaviorComponent>(e).scrapTarget.has_value());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -134,10 +140,10 @@ TEST_CASE("ShipSystem: repair_ship spawn has repairTool and repairBehavior, no w
|
||||
|
||||
const entt::entity e = ss.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
REQUIRE(admin.hasAll<RepairTool>(e));
|
||||
REQUIRE(admin.hasAll<RepairBehavior>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<Weapon>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<SalvageCargo>(e));
|
||||
REQUIRE(admin.hasAll<RepairToolComponent>(e));
|
||||
REQUIRE(admin.hasAll<RepairBehaviorComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<WeaponComponent>(e));
|
||||
REQUIRE_FALSE(admin.hasAll<SalvageCargoComponent>(e));
|
||||
}
|
||||
|
||||
TEST_CASE("ShipSystem: repair_ship level 1 repair stats match config formulas", "[ship]")
|
||||
@@ -149,9 +155,9 @@ TEST_CASE("ShipSystem: repair_ship level 1 repair stats match config formulas",
|
||||
const entt::entity e = ss.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f));
|
||||
|
||||
// repair_rate_formula = "5 + x" at x=1 → 6
|
||||
REQUIRE(admin.get<RepairTool>(e).ratePerTick == Approx(6.0f));
|
||||
REQUIRE(admin.get<RepairToolComponent>(e).ratePerTick == Approx(6.0f));
|
||||
// repair_range_formula = "80"
|
||||
REQUIRE(admin.get<RepairTool>(e).range == Approx(80.0f));
|
||||
REQUIRE(admin.get<RepairToolComponent>(e).range == Approx(80.0f));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
#include "BuildingSystem.h"
|
||||
#include "BuildingType.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "EcsComponents.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "FactionComponent.h"
|
||||
#include "GameConfig.h"
|
||||
#include "ItemType.h"
|
||||
#include "Rotation.h"
|
||||
#include "Ship.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "Simulation.h"
|
||||
#include "Tick.h"
|
||||
@@ -55,8 +55,8 @@ static BuildingId placeShipyard(Simulation& sim, const BuildingDef& yardDef)
|
||||
static int countShips(Simulation& sim)
|
||||
{
|
||||
int n = 0;
|
||||
sim.admin().forEach<ShipIdentity>(
|
||||
[&n](entt::entity /*e*/, const ShipIdentity& /*si*/) { ++n; });
|
||||
sim.admin().forEach<ShipIdentityComponent>(
|
||||
[&n](entt::entity /*e*/, const ShipIdentityComponent& /*si*/) { ++n; });
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -114,8 +114,8 @@ TEST_CASE("Shipyard: spawns a player ship after production cycle completes",
|
||||
REQUIRE(countShips(sim) == shipsBefore + 1);
|
||||
|
||||
bool foundPlayerShip = false;
|
||||
sim.admin().forEach<ShipIdentity, Faction>(
|
||||
[&](entt::entity /*e*/, const ShipIdentity& si, const Faction& f)
|
||||
sim.admin().forEach<ShipIdentityComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const ShipIdentityComponent& si, const FactionComponent& f)
|
||||
{
|
||||
if (!f.isEnemy && si.schematicId == def->id)
|
||||
{
|
||||
|
||||
@@ -6,11 +6,15 @@
|
||||
#include "BuildingSystem.h"
|
||||
#include "BuildingType.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "EcsComponents.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "FactionComponent.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "HqProxyComponent.h"
|
||||
#include "Rotation.h"
|
||||
#include "Ship.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "StationBodyComponent.h"
|
||||
#include "WeaponComponent.h"
|
||||
#include "Simulation.h"
|
||||
#include "Tick.h"
|
||||
#include "WaveSystem.h"
|
||||
@@ -112,8 +116,8 @@ TEST_CASE("WaveSystem: Simulation pre-places HQ + 2 player + 2 enemy stations",
|
||||
// Stations are ECS entities.
|
||||
int playerCount = 0;
|
||||
int enemyCount = 0;
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy) { ++enemyCount; }
|
||||
else { ++playerCount; }
|
||||
@@ -132,8 +136,8 @@ TEST_CASE("WaveSystem: HQ has correct initial HP from config", "[wave]")
|
||||
static_cast<float>(sim.config().stations.hq.hpFormula.evaluate(0.0));
|
||||
bool found = false;
|
||||
float actualHp = 0.0f;
|
||||
sim.admin().forEach<HqProxy, Health>(
|
||||
[&](entt::entity /*e*/, const HqProxy& /*hq*/, const Health& h)
|
||||
sim.admin().forEach<HqProxyComponent, HealthComponent>(
|
||||
[&](entt::entity /*e*/, const HqProxyComponent& /*hq*/, const HealthComponent& h)
|
||||
{
|
||||
found = true;
|
||||
actualHp = h.hp;
|
||||
@@ -165,9 +169,9 @@ TEST_CASE("WaveSystem: player stations have weapon set", "[wave]")
|
||||
const Simulation sim(loadConfig(), 42);
|
||||
|
||||
int armedPlayerStations = 0;
|
||||
sim.admin().forEach<StationBody, Faction, Weapon>(
|
||||
[&](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f,
|
||||
const Weapon& w)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, WeaponComponent>(
|
||||
[&](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f,
|
||||
const WeaponComponent& w)
|
||||
{
|
||||
if (!f.isEnemy)
|
||||
{
|
||||
@@ -185,9 +189,9 @@ TEST_CASE("WaveSystem: enemy stations have weapon set", "[wave]")
|
||||
const Simulation sim(loadConfig(), 42);
|
||||
|
||||
int armedEnemyStations = 0;
|
||||
sim.admin().forEach<StationBody, Faction, Weapon>(
|
||||
[&](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f,
|
||||
const Weapon& w)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, WeaponComponent>(
|
||||
[&](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f,
|
||||
const WeaponComponent& w)
|
||||
{
|
||||
if (f.isEnemy)
|
||||
{
|
||||
@@ -217,8 +221,8 @@ TEST_CASE("WaveSystem: enemy ships spawn after the initial gap elapses", "[wave]
|
||||
}
|
||||
|
||||
bool foundEnemyShip = false;
|
||||
sim.admin().forEach<ShipIdentity, Faction>(
|
||||
[&](entt::entity /*e*/, const ShipIdentity& /*si*/, const Faction& f)
|
||||
sim.admin().forEach<ShipIdentityComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const ShipIdentityComponent& /*si*/, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy) { foundEnemyShip = true; }
|
||||
});
|
||||
@@ -236,8 +240,8 @@ TEST_CASE("WaveSystem: only eligible ships (cost > 0) appear in waves", "[wave]"
|
||||
sim.tick();
|
||||
}
|
||||
|
||||
sim.admin().forEach<ShipIdentity, Faction>(
|
||||
[&](entt::entity /*e*/, const ShipIdentity& si, const Faction& f)
|
||||
sim.admin().forEach<ShipIdentityComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const ShipIdentityComponent& si, const FactionComponent& f)
|
||||
{
|
||||
if (!f.isEnemy) { return; }
|
||||
// salvage_ship and repair_ship have cost_formula = "0" and must not spawn.
|
||||
@@ -255,8 +259,8 @@ TEST_CASE("WaveSystem: destroying both enemy stations triggers a push", "[wave]"
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
// Damage both enemy stations to 0.
|
||||
sim.admin().forEach<StationBody, Faction, Health>(
|
||||
[](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f, Health& h)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h)
|
||||
{
|
||||
if (f.isEnemy) { h.hp = -1.0f; }
|
||||
});
|
||||
@@ -265,8 +269,8 @@ TEST_CASE("WaveSystem: destroying both enemy stations triggers a push", "[wave]"
|
||||
|
||||
// After push: should have 2 new enemy stations.
|
||||
int enemyCount = 0;
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy) { ++enemyCount; }
|
||||
});
|
||||
@@ -277,8 +281,8 @@ TEST_CASE("WaveSystem: push emits exactly one SchematicDropEvent", "[wave]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
sim.admin().forEach<StationBody, Faction, Health>(
|
||||
[](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f, Health& h)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h)
|
||||
{
|
||||
if (f.isEnemy) { h.hp = -1.0f; }
|
||||
});
|
||||
@@ -293,8 +297,8 @@ TEST_CASE("WaveSystem: push schematic drop awards a known ship id", "[wave]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
sim.admin().forEach<StationBody, Faction, Health>(
|
||||
[](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f, Health& h)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h)
|
||||
{
|
||||
if (f.isEnemy) { h.hp = -1.0f; }
|
||||
});
|
||||
@@ -322,8 +326,8 @@ TEST_CASE("WaveSystem: push places new enemy stations further right", "[wave]")
|
||||
|
||||
// Record the X position of the initial enemy stations.
|
||||
int initialX = std::numeric_limits<int>::min();
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity /*e*/, const StationBody& sb, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const StationBodyComponent& sb, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy && sb.anchor.x() > initialX)
|
||||
{
|
||||
@@ -331,16 +335,16 @@ TEST_CASE("WaveSystem: push places new enemy stations further right", "[wave]")
|
||||
}
|
||||
});
|
||||
|
||||
sim.admin().forEach<StationBody, Faction, Health>(
|
||||
[](entt::entity /*e*/, const StationBody& /*sb*/, const Faction& f, Health& h)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h)
|
||||
{
|
||||
if (f.isEnemy) { h.hp = -1.0f; }
|
||||
});
|
||||
sim.tick();
|
||||
|
||||
int newX = std::numeric_limits<int>::min();
|
||||
sim.admin().forEach<StationBody, Faction>(
|
||||
[&](entt::entity /*e*/, const StationBody& sb, const Faction& f)
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent>(
|
||||
[&](entt::entity /*e*/, const StationBodyComponent& sb, const FactionComponent& f)
|
||||
{
|
||||
if (f.isEnemy && sb.anchor.x() > newX)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user