Rename ship/station scrap drop entities to "debris"
This commit is contained in:
@@ -265,7 +265,7 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path)
|
||||
cfg.refundPercentage = static_cast<int>(requireInt(tbl["world"]["refund_percentage"], file, "world.refund_percentage"));
|
||||
cfg.deconstructionTimeSeconds = requireDouble(tbl["world"]["deconstruction_time_seconds"], file, "world.deconstruction_time_seconds");
|
||||
cfg.startingBuildingBlocks = static_cast<int>(requireInt(tbl["world"]["starting_building_blocks"], file, "world.starting_building_blocks"));
|
||||
cfg.scrapDespawnSeconds = requireDouble(tbl["world"]["scrap_despawn_seconds"], file, "world.scrap_despawn_seconds");
|
||||
cfg.debrisDespawnSeconds = requireDouble(tbl["world"]["debris_despawn_seconds"], file, "world.debris_despawn_seconds");
|
||||
cfg.scrapPerThreat = requireDouble(tbl["world"]["scrap_per_threat"], file, "world.scrap_per_threat");
|
||||
cfg.tileSize_m = requireDouble(tbl["world"]["tile_size_m"], file, "world.tile_size_m");
|
||||
cfg.beltSpeed_tps = requireDouble(tbl["world"]["belt_speed_mps"], file, "world.belt_speed_mps") / cfg.tileSize_m;
|
||||
|
||||
@@ -70,8 +70,8 @@ struct WorldConfig
|
||||
int refundPercentage; // REQ-BLD-DECONSTRUCT
|
||||
double deconstructionTimeSeconds; // REQ-BLD-DECON-QUEUE
|
||||
int startingBuildingBlocks; // REQ-HQ-STARTING-BLOCKS
|
||||
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
||||
double scrapPerThreat; // REQ-RES-SCRAP-DROP, REQ-THREAT-SCRAP (scrap dropped per unit threat)
|
||||
double debrisDespawnSeconds; // REQ-RES-DEBRIS-DROP
|
||||
double scrapPerThreat; // REQ-RES-DEBRIS-DROP, REQ-THREAT-SCRAP (scrap dropped per unit threat)
|
||||
double tileSize_m; // metres per tile (REQ-GW-TILE-SIZE)
|
||||
double beltSpeed_tps; // REQ-GW-BELT-SPEED (tiles/s, converted from m/s in config)
|
||||
int tunnelMaxDistance_tiles; // REQ-BLD-TUNNEL-PAIR
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "HqProxyComponent.h"
|
||||
#include "MovementIntentComponent.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "DebrisComponent.h"
|
||||
#include "SensorRangeComponent.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "StationBodyComponent.h"
|
||||
@@ -80,11 +80,11 @@ entt::entity EntityAdmin::spawnStation(QPoint anchor, QSize footprint,
|
||||
return entity;
|
||||
}
|
||||
|
||||
entt::entity EntityAdmin::spawnScrap(QVector2D position, int amount, Tick despawnAt)
|
||||
entt::entity EntityAdmin::spawnDebris(QVector2D position, int amount, Tick despawnAt)
|
||||
{
|
||||
entt::entity entity = createEntity();
|
||||
add<PositionComponent>(entity, PositionComponent{position});
|
||||
add<ScrapDataComponent>(entity, ScrapDataComponent{amount});
|
||||
add<DebrisComponent>(entity, DebrisComponent{amount});
|
||||
add<DespawnAtComponent>(entity, DespawnAtComponent{despawnAt});
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
const std::vector<QPoint>& bodyCells,
|
||||
float hp, float maxHp, bool isEnemy);
|
||||
|
||||
entt::entity spawnScrap(QVector2D position, int amount, Tick despawnAt);
|
||||
entt::entity spawnDebris(QVector2D position, int amount, Tick despawnAt);
|
||||
|
||||
entt::entity spawnHqProxy(QVector2D position, float hp, float maxHp);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/RetreatBehavior.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SalvagerComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SalvageScrapBehavior.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ScrapDataComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DebrisComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SelectedBehaviorComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SensorRangeComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipIdentityComponent.h
|
||||
|
||||
8
src/lib/ecs/component/DebrisComponent.h
Normal file
8
src/lib/ecs/component/DebrisComponent.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
// Marks a piece of debris and holds the amount of scrap it still contains
|
||||
// (REQ-RES-DEBRIS-DROP). Salvage modules collect one scrap per cycle (REQ-SHP-SALVAGE).
|
||||
struct DebrisComponent
|
||||
{
|
||||
int amount;
|
||||
};
|
||||
@@ -5,10 +5,10 @@
|
||||
#include <QVector2D>
|
||||
|
||||
// Collect-scrap behavior (one half of the old SalvageBehaviorComponent). The
|
||||
// evaluator finds the nearest scrap and sets scrapTarget when cargo is not full.
|
||||
// evaluator finds the nearest debris and sets debrisTarget when cargo is not full.
|
||||
struct SalvageScrapBehavior
|
||||
{
|
||||
std::optional<QVector2D> scrapTarget;
|
||||
std::optional<QVector2D> debrisTarget;
|
||||
float maxCollectionRange_tiles = 0.0f;
|
||||
float orbitRadius_tiles = 0.0f; // REQ-SHP-ORBIT
|
||||
float score = 0.0f;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct ScrapDataComponent
|
||||
{
|
||||
int amount;
|
||||
};
|
||||
@@ -6,6 +6,6 @@ struct ShipIdentityComponent
|
||||
{
|
||||
std::string schematicId;
|
||||
// Scrap dropped on destruction, derived from the ship's as-built threat cost
|
||||
// at spawn time (REQ-RES-SCRAP-DROP).
|
||||
// at spawn time (REQ-RES-DEBRIS-DROP).
|
||||
int scrapDrop = 0;
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ AiSystem::AiSystem(const GameConfig& config)
|
||||
}
|
||||
|
||||
void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
||||
const ScrapSystem& scraps)
|
||||
const DebrisSystem& debris)
|
||||
{
|
||||
TRACE();
|
||||
|
||||
@@ -54,7 +54,7 @@ void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
||||
m_retreatEvaluator.evaluate(admin);
|
||||
m_attackEvaluator.evaluate(admin);
|
||||
m_repairEvaluator.evaluate(admin);
|
||||
m_salvageScrapEvaluator.evaluate(admin, scraps);
|
||||
m_salvageScrapEvaluator.evaluate(admin, debris);
|
||||
m_deliverScrapEvaluator.evaluate(admin, buildings);
|
||||
|
||||
// Phase 2: pick the highest-scoring behavior per ship.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
class BuildingSystem;
|
||||
class EntityAdmin;
|
||||
class ScrapSystem;
|
||||
class DebrisSystem;
|
||||
struct GameConfig;
|
||||
|
||||
// Orchestrates ship-behavior decision-making in three batched phases:
|
||||
@@ -34,7 +34,7 @@ class AiSystem
|
||||
public:
|
||||
explicit AiSystem(const GameConfig& config);
|
||||
|
||||
void tick(EntityAdmin& admin, const BuildingSystem& buildings, const ScrapSystem& scraps);
|
||||
void tick(EntityAdmin& admin, const BuildingSystem& buildings, const DebrisSystem& debris);
|
||||
|
||||
private:
|
||||
void selectWinningBehaviors(EntityAdmin& admin);
|
||||
|
||||
@@ -23,7 +23,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MovementIntentSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/RepairSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SalvagerSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ScrapSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DebrisSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipSystem.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -53,7 +53,7 @@ SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MovementIntentSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/RepairSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SalvagerSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ScrapSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DebrisSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipSystem.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
78
src/lib/ecs/system/DebrisSystem.cpp
Normal file
78
src/lib/ecs/system/DebrisSystem.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "DebrisSystem.h"
|
||||
|
||||
#include "DespawnAtComponent.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "DebrisComponent.h"
|
||||
#include "tracing.h"
|
||||
|
||||
DebrisSystem::DebrisSystem(EntityAdmin& admin)
|
||||
: m_admin(admin)
|
||||
{
|
||||
}
|
||||
|
||||
entt::entity DebrisSystem::spawn(QVector2D position, int amount, Tick despawnAt)
|
||||
{
|
||||
return m_admin.spawnDebris(position, amount, despawnAt);
|
||||
}
|
||||
|
||||
void DebrisSystem::tickDespawn(Tick currentTick)
|
||||
{
|
||||
TRACE();
|
||||
std::vector<entt::entity> expired;
|
||||
m_admin.forEach<DespawnAtComponent>(
|
||||
[&expired, currentTick](entt::entity e, DespawnAtComponent& d)
|
||||
{
|
||||
if (d.tick <= currentTick)
|
||||
{
|
||||
expired.push_back(e);
|
||||
}
|
||||
});
|
||||
|
||||
for (entt::entity e : expired)
|
||||
{
|
||||
m_admin.destroy(e);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> DebrisSystem::consume(entt::entity entity)
|
||||
{
|
||||
if (!m_admin.isValid(entity) || !m_admin.hasAll<DebrisComponent>(entity))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
int amount = m_admin.get<DebrisComponent>(entity).amount;
|
||||
m_admin.destroy(entity);
|
||||
return amount;
|
||||
}
|
||||
|
||||
bool DebrisSystem::collectOne(entt::entity entity)
|
||||
{
|
||||
if (!m_admin.isValid(entity) || !m_admin.hasAll<DebrisComponent>(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
DebrisComponent& data = m_admin.get<DebrisComponent>(entity);
|
||||
if (data.amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
--data.amount;
|
||||
if (data.amount <= 0)
|
||||
{
|
||||
m_admin.destroy(entity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<DebrisInfo> DebrisSystem::getAllDebrisInfo() const
|
||||
{
|
||||
std::vector<DebrisInfo> result;
|
||||
m_admin.forEach<DebrisComponent>(
|
||||
[&result, this](entt::entity e, const DebrisComponent& sd)
|
||||
{
|
||||
result.push_back(DebrisInfo{e, m_admin.get<PositionComponent>(e).value, sd.amount});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -11,31 +11,35 @@
|
||||
|
||||
class EntityAdmin;
|
||||
|
||||
struct ScrapInfo
|
||||
// A piece of debris and the scrap amount it still holds (REQ-RES-DEBRIS-DROP).
|
||||
struct DebrisInfo
|
||||
{
|
||||
entt::entity entity;
|
||||
QVector2D position;
|
||||
int amount;
|
||||
};
|
||||
|
||||
class ScrapSystem
|
||||
// Manages debris entities: the salvageable objects dropped by destroyed ships and
|
||||
// defence stations (REQ-RES-DEBRIS-DROP). Each piece carries a scrap amount that
|
||||
// salvage modules collect one unit at a time (REQ-SHP-SALVAGE).
|
||||
class DebrisSystem
|
||||
{
|
||||
public:
|
||||
explicit ScrapSystem(EntityAdmin& admin);
|
||||
explicit DebrisSystem(EntityAdmin& admin);
|
||||
|
||||
entt::entity spawn(QVector2D position, int amount, Tick despawnAt);
|
||||
void tickDespawn(Tick currentTick);
|
||||
|
||||
// Removes the scrap and returns its amount, or nullopt if not found.
|
||||
// Removes the debris and returns its remaining scrap amount, or nullopt if not found.
|
||||
std::optional<int> consume(entt::entity entity);
|
||||
|
||||
// Collects a single scrap unit from the pile: decrements its amount by one,
|
||||
// Collects a single scrap unit from the debris: decrements its amount by one,
|
||||
// destroying the entity once depleted. Returns true if a scrap was collected,
|
||||
// false if the entity is invalid or already empty (REQ-SHP-SALVAGE).
|
||||
bool collectOne(entt::entity entity);
|
||||
|
||||
// Lightweight snapshot for callers that need to iterate all scrap.
|
||||
std::vector<ScrapInfo> getAllScrapInfo() const;
|
||||
// Lightweight snapshot for callers that need to iterate all debris.
|
||||
std::vector<DebrisInfo> getAllDebrisInfo() const;
|
||||
|
||||
private:
|
||||
EntityAdmin& m_admin;
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "ModuleOwnerComponent.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "SalvagerComponent.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "ScrapSystem.h"
|
||||
#include "DebrisComponent.h"
|
||||
#include "DebrisSystem.h"
|
||||
#include "tracing.h"
|
||||
|
||||
SalvagerSystem::SalvagerSystem(EntityAdmin& admin)
|
||||
@@ -23,14 +23,14 @@ SalvagerSystem::SalvagerSystem(EntityAdmin& admin)
|
||||
{
|
||||
}
|
||||
|
||||
void SalvagerSystem::tick(Tick currentTick, ScrapSystem& scraps, BuildingSystem& buildings,
|
||||
void SalvagerSystem::tick(Tick currentTick, DebrisSystem& debris, BuildingSystem& buildings,
|
||||
std::vector<BeamFiredEvent>& outBeamFiredEvents)
|
||||
{
|
||||
TRACE();
|
||||
// Apply collections whose mid-beam delay has elapsed (cycles started earlier).
|
||||
applyPendingCollections(currentTick, scraps);
|
||||
applyPendingCollections(currentTick, debris);
|
||||
|
||||
const std::vector<ScrapInfo> allScrap = scraps.getAllScrapInfo();
|
||||
const std::vector<DebrisInfo> allDebris = debris.getAllDebrisInfo();
|
||||
|
||||
// Tick down per-module collection cooldowns.
|
||||
m_admin.forEach<SalvagerComponent>(
|
||||
@@ -40,8 +40,8 @@ void SalvagerSystem::tick(Tick currentTick, ScrapSystem& scraps, BuildingSystem&
|
||||
});
|
||||
|
||||
// Scrap units already claimed by not-yet-applied collection cycles, so two
|
||||
// modules don't both target the last unit of the same pile (the claim would be
|
||||
// dropped at apply time). A pile is available while its amount exceeds its claims.
|
||||
// modules don't both target the last unit of the same debris (the claim would be
|
||||
// dropped at apply time). Debris is available while its amount exceeds its claims.
|
||||
std::map<entt::entity, int> claimedUnits;
|
||||
// Collection cycles already in flight toward each ship's shared cargo pool, so
|
||||
// concurrent modules on the same ship never start more cycles than the remaining
|
||||
@@ -49,7 +49,7 @@ void SalvagerSystem::tick(Tick currentTick, ScrapSystem& scraps, BuildingSystem&
|
||||
std::map<entt::entity, int> pendingByShip;
|
||||
for (const PendingCollection& pc : m_pendingCollections)
|
||||
{
|
||||
++claimedUnits[pc.scrap];
|
||||
++claimedUnits[pc.debris];
|
||||
++pendingByShip[pc.ship];
|
||||
}
|
||||
|
||||
@@ -66,12 +66,12 @@ void SalvagerSystem::tick(Tick currentTick, ScrapSystem& scraps, BuildingSystem&
|
||||
if (cargo.current + pendingByShip[o.owner] >= cargo.maxCapacity) { return; }
|
||||
|
||||
const QVector2D ownerPos = m_admin.get<PositionComponent>(o.owner).value;
|
||||
for (const ScrapInfo& si : allScrap)
|
||||
for (const DebrisInfo& si : allDebris)
|
||||
{
|
||||
if ((si.position - ownerPos).length() > s.collectionRange_tiles) { continue; }
|
||||
if (claimedUnits[si.entity] >= m_admin.get<ScrapDataComponent>(si.entity).amount)
|
||||
if (claimedUnits[si.entity] >= m_admin.get<DebrisComponent>(si.entity).amount)
|
||||
{
|
||||
continue; // every remaining unit of this pile is already spoken for
|
||||
continue; // every remaining unit of this debris is already spoken for
|
||||
}
|
||||
outBeamFiredEvents.push_back(
|
||||
BeamFiredEvent{BeamKind::Salvage, o.owner, si.entity, currentTick});
|
||||
@@ -107,7 +107,7 @@ void SalvagerSystem::tick(Tick currentTick, ScrapSystem& scraps, BuildingSystem&
|
||||
});
|
||||
}
|
||||
|
||||
void SalvagerSystem::applyPendingCollections(Tick currentTick, ScrapSystem& scraps)
|
||||
void SalvagerSystem::applyPendingCollections(Tick currentTick, DebrisSystem& debris)
|
||||
{
|
||||
std::vector<PendingCollection>::iterator it = m_pendingCollections.begin();
|
||||
while (it != m_pendingCollections.end())
|
||||
@@ -117,7 +117,7 @@ void SalvagerSystem::applyPendingCollections(Tick currentTick, ScrapSystem& scra
|
||||
if (m_admin.isValid(it->ship) && m_admin.hasAll<CargoComponent>(it->ship))
|
||||
{
|
||||
CargoComponent& cargo = m_admin.get<CargoComponent>(it->ship);
|
||||
if (cargo.current < cargo.maxCapacity && scraps.collectOne(it->scrap))
|
||||
if (cargo.current < cargo.maxCapacity && debris.collectOne(it->debris))
|
||||
{
|
||||
++cargo.current;
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
class BuildingSystem;
|
||||
class EntityAdmin;
|
||||
class ScrapSystem;
|
||||
class DebrisSystem;
|
||||
|
||||
// World-mutation system for salvage modules: each module runs a collection cycle
|
||||
// on its own cooldown. When a cycle starts it emits a salvage beam toward an
|
||||
// in-range scrap pile and schedules the collection of one scrap for mid-beam
|
||||
// in-range piece of debris and schedules the collection of one scrap for mid-beam
|
||||
// (kBeamImpactDelayTicks later) — mirroring weapon firing. Also delivers full
|
||||
// cargo at a SalvageBay. Runs every tick, independent of behavior selection.
|
||||
class SalvagerSystem
|
||||
@@ -21,18 +21,18 @@ class SalvagerSystem
|
||||
public:
|
||||
explicit SalvagerSystem(EntityAdmin& admin);
|
||||
|
||||
void tick(Tick currentTick, ScrapSystem& scraps, BuildingSystem& buildings,
|
||||
void tick(Tick currentTick, DebrisSystem& debris, BuildingSystem& buildings,
|
||||
std::vector<BeamFiredEvent>& outBeamFiredEvents);
|
||||
|
||||
private:
|
||||
struct PendingCollection
|
||||
{
|
||||
entt::entity ship;
|
||||
entt::entity scrap;
|
||||
entt::entity debris;
|
||||
Tick appliesAt;
|
||||
};
|
||||
|
||||
void applyPendingCollections(Tick currentTick, ScrapSystem& scraps);
|
||||
void applyPendingCollections(Tick currentTick, DebrisSystem& debris);
|
||||
|
||||
EntityAdmin& m_admin;
|
||||
std::vector<PendingCollection> m_pendingCollections;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
#include "ScrapSystem.h"
|
||||
|
||||
#include "DespawnAtComponent.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "tracing.h"
|
||||
|
||||
ScrapSystem::ScrapSystem(EntityAdmin& admin)
|
||||
: m_admin(admin)
|
||||
{
|
||||
}
|
||||
|
||||
entt::entity ScrapSystem::spawn(QVector2D position, int amount, Tick despawnAt)
|
||||
{
|
||||
return m_admin.spawnScrap(position, amount, despawnAt);
|
||||
}
|
||||
|
||||
void ScrapSystem::tickDespawn(Tick currentTick)
|
||||
{
|
||||
TRACE();
|
||||
std::vector<entt::entity> expired;
|
||||
m_admin.forEach<DespawnAtComponent>(
|
||||
[&expired, currentTick](entt::entity e, DespawnAtComponent& d)
|
||||
{
|
||||
if (d.tick <= currentTick)
|
||||
{
|
||||
expired.push_back(e);
|
||||
}
|
||||
});
|
||||
|
||||
for (entt::entity e : expired)
|
||||
{
|
||||
m_admin.destroy(e);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> ScrapSystem::consume(entt::entity entity)
|
||||
{
|
||||
if (!m_admin.isValid(entity) || !m_admin.hasAll<ScrapDataComponent>(entity))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
int amount = m_admin.get<ScrapDataComponent>(entity).amount;
|
||||
m_admin.destroy(entity);
|
||||
return amount;
|
||||
}
|
||||
|
||||
bool ScrapSystem::collectOne(entt::entity entity)
|
||||
{
|
||||
if (!m_admin.isValid(entity) || !m_admin.hasAll<ScrapDataComponent>(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ScrapDataComponent& data = m_admin.get<ScrapDataComponent>(entity);
|
||||
if (data.amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
--data.amount;
|
||||
if (data.amount <= 0)
|
||||
{
|
||||
m_admin.destroy(entity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<ScrapInfo> ScrapSystem::getAllScrapInfo() const
|
||||
{
|
||||
std::vector<ScrapInfo> result;
|
||||
m_admin.forEach<ScrapDataComponent>(
|
||||
[&result, this](entt::entity e, const ScrapDataComponent& sd)
|
||||
{
|
||||
result.push_back(ScrapInfo{e, m_admin.get<PositionComponent>(e).value, sd.amount});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ entt::entity ShipSystem::spawn(const std::string& schematicId,
|
||||
layout.has_value() ? layout->placedModules : def->defaultModules;
|
||||
|
||||
// Derive the scrap dropped on destruction from the ship's as-built threat cost
|
||||
// (REQ-RES-SCRAP-DROP): round(threat * scrap_per_threat), floored at 1 for any
|
||||
// (REQ-RES-DEBRIS-DROP): round(threat * scrap_per_threat), floored at 1 for any
|
||||
// ship with threat > 0. Computed once here since threat is level-independent.
|
||||
const double threatCost = calculateShipThreatCost(m_config.threatCosts, m_config,
|
||||
schematicId, modules);
|
||||
@@ -392,7 +392,7 @@ entt::entity ShipSystem::spawn(const std::string& schematicId,
|
||||
}
|
||||
|
||||
SalvageScrapBehavior salvage;
|
||||
salvage.scrapTarget = std::nullopt;
|
||||
salvage.debrisTarget = std::nullopt;
|
||||
salvage.maxCollectionRange_tiles = maxCollRange;
|
||||
salvage.orbitRadius_tiles =
|
||||
maxCollRange * static_cast<float>(m_config.world.orbitFactor);
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
#include "EntityAdmin.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "SalvageScrapBehavior.h"
|
||||
#include "ScrapSystem.h"
|
||||
#include "DebrisSystem.h"
|
||||
#include "SensorRangeComponent.h"
|
||||
#include "tracing.h"
|
||||
|
||||
void SalvageScrapEvaluator::evaluate(EntityAdmin& admin, const ScrapSystem& scraps)
|
||||
void SalvageScrapEvaluator::evaluate(EntityAdmin& admin, const DebrisSystem& debris)
|
||||
{
|
||||
TRACE();
|
||||
const std::unordered_map<entt::entity, CargoState> cargoByShip = buildCargoByShip(admin);
|
||||
const std::vector<ScrapInfo> allScrap = scraps.getAllScrapInfo();
|
||||
const std::vector<DebrisInfo> allDebris = debris.getAllDebrisInfo();
|
||||
|
||||
admin.forEach<SalvageScrapBehavior, PositionComponent, SensorRangeComponent>(
|
||||
[&](entt::entity e, SalvageScrapBehavior& salvage, const PositionComponent& pos,
|
||||
@@ -31,15 +31,15 @@ void SalvageScrapEvaluator::evaluate(EntityAdmin& admin, const ScrapSystem& scra
|
||||
|
||||
if (cargoFull)
|
||||
{
|
||||
salvage.scrapTarget = std::nullopt;
|
||||
salvage.debrisTarget = std::nullopt;
|
||||
salvage.score = BehaviorScores::kInactive;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find nearest scrap within sensor range.
|
||||
// Find nearest debris within sensor range.
|
||||
float bestDist = sensor.value_tiles;
|
||||
std::optional<QVector2D> bestPos;
|
||||
for (const ScrapInfo& si : allScrap)
|
||||
for (const DebrisInfo& si : allDebris)
|
||||
{
|
||||
const float dist = (si.position - pos.value).length();
|
||||
if (dist < bestDist)
|
||||
@@ -49,7 +49,7 @@ void SalvageScrapEvaluator::evaluate(EntityAdmin& admin, const ScrapSystem& scra
|
||||
}
|
||||
}
|
||||
|
||||
salvage.scrapTarget = bestPos;
|
||||
salvage.debrisTarget = bestPos;
|
||||
salvage.score = bestPos ? BehaviorScores::kSalvage : BehaviorScores::kInactive;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
class EntityAdmin;
|
||||
class ScrapSystem;
|
||||
class DebrisSystem;
|
||||
|
||||
// When cargo is not full, finds the nearest scrap within sensor range and sets
|
||||
// it as the target, scoring high. Scores inactive when cargo is full or no scrap
|
||||
// When cargo is not full, finds the nearest debris within sensor range and sets
|
||||
// it as the target, scoring high. Scores inactive when cargo is full or no debris
|
||||
// is in range (Advance then handles roaming).
|
||||
class SalvageScrapEvaluator
|
||||
{
|
||||
public:
|
||||
void evaluate(EntityAdmin& admin, const ScrapSystem& scraps);
|
||||
void evaluate(EntityAdmin& admin, const DebrisSystem& debris);
|
||||
};
|
||||
|
||||
@@ -17,8 +17,8 @@ void SalvageScrapExecutor::execute(EntityAdmin& admin)
|
||||
MovementIntentComponent& intent)
|
||||
{
|
||||
if (selected.winner != BehaviorKind::SalvageScrap) { return; }
|
||||
if (!salvage.scrapTarget) { return; }
|
||||
intent = MovementIntentComponent{true, *salvage.scrapTarget,
|
||||
if (!salvage.debrisTarget) { return; }
|
||||
intent = MovementIntentComponent{true, *salvage.debrisTarget,
|
||||
salvage.orbitRadius_tiles};
|
||||
});
|
||||
}
|
||||
|
||||
18
src/lib/eventsystem/event/DebrisSelectionChangedEvent.h
Normal file
18
src/lib/eventsystem/event/DebrisSelectionChangedEvent.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "entt/entity/entity.hpp"
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
// The set of currently selected debris (REQ-UI-DEBRIS-CLICK-SELECT,
|
||||
// REQ-UI-DEBRIS-MULTI-SELECT). An empty list means no debris is selected. Debris forms
|
||||
// its own selection category, mutually exclusive with buildings and entities.
|
||||
class DebrisSelectionChangedEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit DebrisSelectionChangedEvent(std::vector<entt::entity> debris)
|
||||
: debris(std::move(debris)) {}
|
||||
const std::vector<entt::entity> debris;
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "entt/entity/entity.hpp"
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
// The set of currently selected scrap piles (REQ-UI-SCRAP-CLICK-SELECT,
|
||||
// REQ-UI-SCRAP-MULTI-SELECT). An empty list means no scrap is selected. Scrap forms
|
||||
// its own selection category, mutually exclusive with buildings and entities.
|
||||
class ScrapSelectionChangedEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit ScrapSelectionChangedEvent(std::vector<entt::entity> scrap)
|
||||
: scrap(std::move(scrap)) {}
|
||||
const std::vector<entt::entity> scrap;
|
||||
};
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "EntityAdmin.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "DebrisComponent.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "StationBodyComponent.h"
|
||||
#include "HealthComponent.h"
|
||||
@@ -58,16 +58,16 @@ entt::entity entityAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
|
||||
return bestShip;
|
||||
}
|
||||
|
||||
entt::entity scrapAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
|
||||
entt::entity debrisAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
|
||||
{
|
||||
// Slightly larger than the scrap's rendered radius (0.2 tiles) so small piles
|
||||
// Slightly larger than the debris's rendered radius (0.2 tiles) so small pieces
|
||||
// remain easy to click; tunable.
|
||||
constexpr float kScrapHitRadiusSquared = 0.35f * 0.35f;
|
||||
entt::entity bestScrap = entt::null;
|
||||
float bestDistSquared = kScrapHitRadiusSquared;
|
||||
constexpr float kDebrisHitRadiusSquared = 0.35f * 0.35f;
|
||||
entt::entity bestDebris = entt::null;
|
||||
float bestDistSquared = kDebrisHitRadiusSquared;
|
||||
|
||||
admin.forEach<ScrapDataComponent, PositionComponent>(
|
||||
[&](entt::entity entity, const ScrapDataComponent& /*sd*/, const PositionComponent& pos)
|
||||
admin.forEach<DebrisComponent, PositionComponent>(
|
||||
[&](entt::entity entity, const DebrisComponent& /*sd*/, const PositionComponent& pos)
|
||||
{
|
||||
const float dx = pos.value.x() - worldPos.x();
|
||||
const float dy = pos.value.y() - worldPos.y();
|
||||
@@ -75,14 +75,14 @@ entt::entity scrapAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
|
||||
if (distSquared < bestDistSquared)
|
||||
{
|
||||
bestDistSquared = distSquared;
|
||||
bestScrap = entity;
|
||||
bestDebris = entity;
|
||||
}
|
||||
});
|
||||
|
||||
return bestScrap;
|
||||
return bestDebris;
|
||||
}
|
||||
|
||||
std::vector<entt::entity> scrapInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB)
|
||||
std::vector<entt::entity> debrisInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB)
|
||||
{
|
||||
const int minX = std::min(tileA.x(), tileB.x());
|
||||
const int maxX = std::max(tileA.x(), tileB.x());
|
||||
@@ -90,8 +90,8 @@ std::vector<entt::entity> scrapInBox(EntityAdmin& admin, QPoint tileA, QPoint ti
|
||||
const int maxY = std::max(tileA.y(), tileB.y());
|
||||
|
||||
std::vector<entt::entity> result;
|
||||
admin.forEach<ScrapDataComponent, PositionComponent>(
|
||||
[&](entt::entity entity, const ScrapDataComponent& /*sd*/, const PositionComponent& pos)
|
||||
admin.forEach<DebrisComponent, PositionComponent>(
|
||||
[&](entt::entity entity, const DebrisComponent& /*sd*/, const PositionComponent& pos)
|
||||
{
|
||||
const int tileX = static_cast<int>(std::floor(pos.value.x()));
|
||||
const int tileY = static_cast<int>(std::floor(pos.value.y()));
|
||||
|
||||
@@ -11,14 +11,14 @@ class EntityAdmin;
|
||||
|
||||
entt::entity entityAtWorldPos(EntityAdmin& admin, QVector2D worldPos);
|
||||
|
||||
// Returns the nearest scrap pile whose center is within the scrap pick radius of
|
||||
// worldPos, or entt::null if none (REQ-UI-SCRAP-CLICK-SELECT). Scrap is picked only
|
||||
// after actors: entityAtWorldPos never returns scrap (scrap has no HealthComponent).
|
||||
entt::entity scrapAtWorldPos(EntityAdmin& admin, QVector2D worldPos);
|
||||
// Returns the nearest piece of debris whose center is within the debris pick radius of
|
||||
// worldPos, or entt::null if none (REQ-UI-DEBRIS-CLICK-SELECT). Debris is picked only
|
||||
// after actors: entityAtWorldPos never returns debris (debris has no HealthComponent).
|
||||
entt::entity debrisAtWorldPos(EntityAdmin& admin, QVector2D worldPos);
|
||||
|
||||
// Returns every scrap pile whose position falls within the inclusive tile rectangle
|
||||
// spanned by tileA and tileB, in any corner order (REQ-UI-SCRAP-MULTI-SELECT).
|
||||
std::vector<entt::entity> scrapInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB);
|
||||
// Returns every piece of debris whose position falls within the inclusive tile rectangle
|
||||
// spanned by tileA and tileB, in any corner order (REQ-UI-DEBRIS-MULTI-SELECT).
|
||||
std::vector<entt::entity> debrisInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB);
|
||||
|
||||
// Returns every living actor (ship or defence station, player or enemy) that falls
|
||||
// within the inclusive tile rectangle spanned by tileA and tileB, in any corner order
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#include "PositionComponent.h"
|
||||
#include "RepairSystem.h"
|
||||
#include "SalvagerSystem.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "ScrapSystem.h"
|
||||
#include "DebrisComponent.h"
|
||||
#include "DebrisSystem.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "StateChecksum.h"
|
||||
@@ -69,7 +69,7 @@ Simulation::Simulation(GameConfig config, unsigned int seed)
|
||||
m_aiSystem = std::make_unique<AiSystem>(m_config);
|
||||
m_movementIntentSystem = std::make_unique<MovementIntentSystem>();
|
||||
m_dynamicBodySystem = std::make_unique<DynamicBodySystem>();
|
||||
m_scrapSystem = std::make_unique<ScrapSystem>(m_admin);
|
||||
m_debrisSystem = std::make_unique<DebrisSystem>(m_admin);
|
||||
m_salvagerSystem = std::make_unique<SalvagerSystem>(m_admin);
|
||||
m_repairSystem = std::make_unique<RepairSystem>(m_admin);
|
||||
m_waveSystem = std::make_unique<WaveSystem>(m_config, m_rng);
|
||||
@@ -141,7 +141,7 @@ void Simulation::reset(unsigned int seed)
|
||||
m_aiSystem = std::make_unique<AiSystem>(m_config);
|
||||
m_movementIntentSystem = std::make_unique<MovementIntentSystem>();
|
||||
m_dynamicBodySystem = std::make_unique<DynamicBodySystem>();
|
||||
m_scrapSystem = std::make_unique<ScrapSystem>(m_admin);
|
||||
m_debrisSystem = std::make_unique<DebrisSystem>(m_admin);
|
||||
m_salvagerSystem = std::make_unique<SalvagerSystem>(m_admin);
|
||||
m_repairSystem = std::make_unique<RepairSystem>(m_admin);
|
||||
m_waveSystem = std::make_unique<WaveSystem>(m_config, m_rng);
|
||||
@@ -329,10 +329,10 @@ void Simulation::tick()
|
||||
m_shipSystem->clearMovementIntents();
|
||||
// Score-based behavior selection: evaluate, select winner, execute (sets
|
||||
// movement intent + preferred module targets only — no world mutation).
|
||||
m_aiSystem->tick(m_admin, *m_buildingSystem, *m_scrapSystem);
|
||||
m_aiSystem->tick(m_admin, *m_buildingSystem, *m_debrisSystem);
|
||||
// Module systems perform the world mutation (collection/delivery, healing).
|
||||
// Each emits its tool beams and applies its own delayed (mid-beam) effects.
|
||||
m_salvagerSystem->tick(m_currentTick, *m_scrapSystem, *m_buildingSystem, m_beamFiredEvents);
|
||||
m_salvagerSystem->tick(m_currentTick, *m_debrisSystem, *m_buildingSystem, m_beamFiredEvents);
|
||||
m_repairSystem->tick(m_currentTick, m_beamFiredEvents);
|
||||
|
||||
// Step 8: combat resolution
|
||||
@@ -352,8 +352,8 @@ void Simulation::tick()
|
||||
m_movementIntentSystem->tick(m_admin);
|
||||
m_dynamicBodySystem->tick(m_admin);
|
||||
|
||||
// Step 11: scrap despawn
|
||||
m_scrapSystem->tickDespawn(m_currentTick);
|
||||
// Step 11: debris despawn
|
||||
m_debrisSystem->tickDespawn(m_currentTick);
|
||||
|
||||
++m_currentTick;
|
||||
}
|
||||
@@ -542,8 +542,8 @@ void Simulation::tickDeathsAndLoot()
|
||||
if (si.scrapDrop > 0)
|
||||
{
|
||||
const Tick despawnAt = m_currentTick
|
||||
+ secondsToTicks(m_config.world.scrapDespawnSeconds);
|
||||
m_scrapSystem->spawn(pos.value, si.scrapDrop, despawnAt);
|
||||
+ secondsToTicks(m_config.world.debrisDespawnSeconds);
|
||||
m_debrisSystem->spawn(pos.value, si.scrapDrop, despawnAt);
|
||||
}
|
||||
m_shipSystem->despawn(deadEntity);
|
||||
}
|
||||
@@ -567,7 +567,7 @@ void Simulation::tickDeathsAndLoot()
|
||||
const FactionComponent& fac = m_admin.get<FactionComponent>(deadEntity);
|
||||
|
||||
const Tick despawnAt = m_currentTick
|
||||
+ secondsToTicks(m_config.world.scrapDespawnSeconds);
|
||||
+ secondsToTicks(m_config.world.debrisDespawnSeconds);
|
||||
int scrap = 0;
|
||||
if (!fac.isEnemy)
|
||||
{
|
||||
@@ -584,7 +584,7 @@ void Simulation::tickDeathsAndLoot()
|
||||
}
|
||||
if (scrap > 0)
|
||||
{
|
||||
m_scrapSystem->spawn(pos.value, scrap, despawnAt);
|
||||
m_debrisSystem->spawn(pos.value, scrap, despawnAt);
|
||||
}
|
||||
m_buildingSystem->unregisterTileOccupancy(sb.bodyCells);
|
||||
{
|
||||
@@ -1000,8 +1000,8 @@ unsigned long long Simulation::computeStateChecksum() const
|
||||
hasher.append(c.linearAcceleration_tptt);
|
||||
hasher.append(c.angularAcceleration_rptt);
|
||||
});
|
||||
m_admin.forEach<ScrapDataComponent>(
|
||||
[&hasher](entt::entity entity, const ScrapDataComponent& c)
|
||||
m_admin.forEach<DebrisComponent>(
|
||||
[&hasher](entt::entity entity, const DebrisComponent& c)
|
||||
{
|
||||
hasher.append(static_cast<std::uint32_t>(entity));
|
||||
hasher.append(c.amount);
|
||||
@@ -1238,14 +1238,14 @@ const ShipSystem& Simulation::getShips() const
|
||||
return *m_shipSystem;
|
||||
}
|
||||
|
||||
ScrapSystem& Simulation::getScraps()
|
||||
DebrisSystem& Simulation::getDebrisSystem()
|
||||
{
|
||||
return *m_scrapSystem;
|
||||
return *m_debrisSystem;
|
||||
}
|
||||
|
||||
const ScrapSystem& Simulation::getScraps() const
|
||||
const DebrisSystem& Simulation::getDebrisSystem() const
|
||||
{
|
||||
return *m_scrapSystem;
|
||||
return *m_debrisSystem;
|
||||
}
|
||||
|
||||
EntityAdmin& Simulation::getAdmin()
|
||||
|
||||
@@ -33,7 +33,7 @@ class MovementIntentSystem;
|
||||
class RepairSystem;
|
||||
class SalvagerSystem;
|
||||
class ShipSystem;
|
||||
class ScrapSystem;
|
||||
class DebrisSystem;
|
||||
class WaveSystem;
|
||||
|
||||
class Simulation: public CombinedEventHandler<TracePrintRequestedEvent>
|
||||
@@ -122,8 +122,8 @@ public:
|
||||
const BeltSystem& getBelts() const;
|
||||
ShipSystem& getShips();
|
||||
const ShipSystem& getShips() const;
|
||||
ScrapSystem& getScraps();
|
||||
const ScrapSystem& getScraps() const;
|
||||
DebrisSystem& getDebrisSystem();
|
||||
const DebrisSystem& getDebrisSystem() const;
|
||||
EntityAdmin& getAdmin();
|
||||
const EntityAdmin& getAdmin() const;
|
||||
|
||||
@@ -172,7 +172,7 @@ private:
|
||||
// Stores their IDs in m_currentEnemyStationIds.
|
||||
void placeEnemyStationSet(int generation);
|
||||
|
||||
// Tick step 9: remove dead ships and buildings, drop scrap, handle push.
|
||||
// Tick step 9: remove dead ships and buildings, drop debris, handle push.
|
||||
void tickDeathsAndLoot();
|
||||
|
||||
// Generate up to 3 schematic choices (REQ-DEF-SCHEMATIC-DROP) for the player.
|
||||
@@ -269,7 +269,7 @@ private:
|
||||
std::unique_ptr<AiSystem> m_aiSystem;
|
||||
std::unique_ptr<MovementIntentSystem> m_movementIntentSystem;
|
||||
std::unique_ptr<DynamicBodySystem> m_dynamicBodySystem;
|
||||
std::unique_ptr<ScrapSystem> m_scrapSystem;
|
||||
std::unique_ptr<DebrisSystem> m_debrisSystem;
|
||||
std::unique_ptr<SalvagerSystem> m_salvagerSystem;
|
||||
std::unique_ptr<RepairSystem> m_repairSystem;
|
||||
std::unique_ptr<WaveSystem> m_waveSystem;
|
||||
|
||||
@@ -66,7 +66,7 @@ ThreatCostTable computeThreatCostTable(const GameConfig& config)
|
||||
ThreatCostTable table;
|
||||
|
||||
// Scrap threat (REQ-THREAT-SCRAP) is the constant inverse of the scrap-drop
|
||||
// conversion (REQ-RES-SCRAP-DROP): one scrap is worth 1 / scrap_per_threat.
|
||||
// conversion (REQ-RES-DEBRIS-DROP): one scrap is worth 1 / scrap_per_threat.
|
||||
// Set it up front so reprocessing-only item threats (below) can use it, and so
|
||||
// it no longer depends on any ship's threat cost.
|
||||
table.scrapThreat = config.world.scrapPerThreat > 0.0
|
||||
|
||||
Reference in New Issue
Block a user