134 lines
5.2 KiB
C++
134 lines
5.2 KiB
C++
#include "SalvagerSystem.h"
|
|
#include "FactoryQueries.h"
|
|
|
|
#include <vector>
|
|
|
|
#include <QVector2D>
|
|
|
|
#include "Building.h"
|
|
#include "BuildingSystem.h"
|
|
#include "CargoComponent.h"
|
|
#include "DeliverScrapBehavior.h"
|
|
#include "EntityAdmin.h"
|
|
#include <map>
|
|
|
|
#include "ModuleOwnerComponent.h"
|
|
#include "PositionComponent.h"
|
|
#include "SalvagerComponent.h"
|
|
#include "DebrisComponent.h"
|
|
#include "DebrisSystem.h"
|
|
#include "tracing.h"
|
|
|
|
SalvagerSystem::SalvagerSystem(EntityAdmin& admin)
|
|
: m_admin(admin)
|
|
{
|
|
}
|
|
|
|
void SalvagerSystem::tick(Tick currentTick, FactoryState& state,
|
|
std::vector<BeamFiredEvent>& outBeamFiredEvents)
|
|
{
|
|
TRACE();
|
|
// Apply collections whose mid-beam delay has elapsed (cycles started earlier).
|
|
applyPendingCollections(currentTick);
|
|
|
|
const std::vector<DebrisInfo> allDebris = getAllDebrisInfo(m_admin);
|
|
|
|
// Tick down per-module collection cooldowns.
|
|
m_admin.forEach<SalvagerComponent>(
|
|
[](entt::entity /*e*/, SalvagerComponent& s)
|
|
{
|
|
if (s.cooldownTicksRemaining > 0) { --s.cooldownTicksRemaining; }
|
|
});
|
|
|
|
// Scrap units already claimed by not-yet-applied collection cycles, so two
|
|
// 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
|
|
// capacity can hold (REQ-SHP-SALVAGE).
|
|
std::map<entt::entity, int> pendingByShip;
|
|
for (const PendingCollection& pc : m_pendingCollections)
|
|
{
|
|
++claimedUnits[pc.debris];
|
|
++pendingByShip[pc.ship];
|
|
}
|
|
|
|
// Cycle start: each ready, in-range module whose ship's pool has free space begins
|
|
// a collection cycle — emit the beam now, collect one scrap mid-beam, start the
|
|
// cooldown now.
|
|
m_admin.forEach<SalvagerComponent, ModuleOwnerComponent>(
|
|
[&](entt::entity /*moduleEntity*/, SalvagerComponent& s, const ModuleOwnerComponent& o)
|
|
{
|
|
if (s.cooldownTicksRemaining > 0 || s.collectionIntervalTicks <= 0) { return; }
|
|
if (!m_admin.hasAll<PositionComponent, CargoComponent>(o.owner)) { return; }
|
|
|
|
const CargoComponent& cargo = m_admin.get<CargoComponent>(o.owner);
|
|
if (cargo.current + pendingByShip[o.owner] >= cargo.maxCapacity) { return; }
|
|
|
|
const QVector2D ownerPos = m_admin.get<PositionComponent>(o.owner).value;
|
|
for (const DebrisInfo& si : allDebris)
|
|
{
|
|
if ((si.position - ownerPos).length() > s.collectionRange_tiles) { continue; }
|
|
if (claimedUnits[si.entity] >= m_admin.get<DebrisComponent>(si.entity).amount)
|
|
{
|
|
continue; // every remaining unit of this debris is already spoken for
|
|
}
|
|
outBeamFiredEvents.push_back(
|
|
BeamFiredEvent{BeamKind::Salvage, o.owner, si.entity, currentTick});
|
|
m_pendingCollections.push_back({o.owner, si.entity,
|
|
currentTick + kBeamImpactDelayTicks});
|
|
++claimedUnits[si.entity];
|
|
++pendingByShip[o.owner];
|
|
s.cooldownTicksRemaining = s.collectionIntervalTicks;
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Delivery: a ship at its assigned bay hands over one unit of cargo per tick.
|
|
m_admin.forEach<DeliverScrapBehavior, PositionComponent>(
|
|
[&](entt::entity ship, const DeliverScrapBehavior& deliver, const PositionComponent& pos)
|
|
{
|
|
if (!deliver.deliveryBay.has_value()) { return; }
|
|
const Building* bay = findBuilding(state, *deliver.deliveryBay);
|
|
if (!bay) { return; }
|
|
|
|
const QVector2D bayCenter(bay->anchor.x() + bay->footprint.width() / 2.0f,
|
|
bay->anchor.y() + bay->footprint.height() / 2.0f);
|
|
if ((pos.value - bayCenter).length() > 1.0f) { return; }
|
|
|
|
// Hand over one unit from the ship's shared cargo pool.
|
|
if (!m_admin.hasAll<CargoComponent>(ship)) { return; }
|
|
CargoComponent& cargo = m_admin.get<CargoComponent>(ship);
|
|
if (cargo.current <= 0) { return; }
|
|
if (deliverScrapToSalvageBay(state, *deliver.deliveryBay))
|
|
{
|
|
--cargo.current;
|
|
}
|
|
});
|
|
}
|
|
|
|
void SalvagerSystem::applyPendingCollections(Tick currentTick)
|
|
{
|
|
std::vector<PendingCollection>::iterator it = m_pendingCollections.begin();
|
|
while (it != m_pendingCollections.end())
|
|
{
|
|
if (it->appliesAt <= currentTick)
|
|
{
|
|
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 && collectOne(m_admin, it->debris))
|
|
{
|
|
++cargo.current;
|
|
}
|
|
}
|
|
it = m_pendingCollections.erase(it);
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
}
|
|
}
|