depend on factory data instead of BuildingSystem in the AI path
This commit is contained in:
@@ -323,9 +323,9 @@ void ArenaSimulation::tick()
|
|||||||
// Ship behavior systems (tick step 7): evaluate, select winner, execute.
|
// Ship behavior systems (tick step 7): evaluate, select winner, execute.
|
||||||
// Module + combat systems emit their tool beams into a shared buffer.
|
// Module + combat systems emit their tool beams into a shared buffer.
|
||||||
m_shipSystem->clearMovementIntents();
|
m_shipSystem->clearMovementIntents();
|
||||||
m_aiSystem->tick(m_admin, *m_buildingSystem, *m_debrisSystem);
|
m_aiSystem->tick(m_admin, m_factoryState, *m_debrisSystem);
|
||||||
std::vector<BeamFiredEvent> beamFiredEvents;
|
std::vector<BeamFiredEvent> beamFiredEvents;
|
||||||
m_salvagerSystem->tick(m_currentTick, *m_debrisSystem, *m_buildingSystem, beamFiredEvents);
|
m_salvagerSystem->tick(m_currentTick, *m_debrisSystem, m_factoryState, beamFiredEvents);
|
||||||
m_repairSystem->tick(m_currentTick, beamFiredEvents);
|
m_repairSystem->tick(m_currentTick, beamFiredEvents);
|
||||||
|
|
||||||
// Combat resolution (tick step 8).
|
// Combat resolution (tick step 8).
|
||||||
|
|||||||
@@ -52,3 +52,18 @@ bool isBeltSubsystemType(BuildingType type)
|
|||||||
|| type == BuildingType::TunnelEntry
|
|| type == BuildingType::TunnelEntry
|
||||||
|| type == BuildingType::TunnelExit;
|
|| type == BuildingType::TunnelExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isProductionBuildingType(BuildingType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BuildingType::Miner:
|
||||||
|
case BuildingType::Smelter:
|
||||||
|
case BuildingType::Assembler:
|
||||||
|
case BuildingType::ReprocessingPlant:
|
||||||
|
case BuildingType::Shipyard:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ std::string buildingTypeId(BuildingType type);
|
|||||||
// they receive, matching against every recipe of their building type.
|
// they receive, matching against every recipe of their building type.
|
||||||
bool isAutoRecipeBuildingType(BuildingType type);
|
bool isAutoRecipeBuildingType(BuildingType type);
|
||||||
|
|
||||||
|
// Buildings that run a production cycle: Miner, Smelter, Assembler, Reprocessing
|
||||||
|
// Plant and Shipyard (REQ-UI-DEBUG-OVERLAY counts these).
|
||||||
|
bool isProductionBuildingType(BuildingType type);
|
||||||
|
|
||||||
// Belts, splitters, and tunnel ends keep their runtime data in the belt subsystem
|
// Belts, splitters, and tunnel ends keep their runtime data in the belt subsystem
|
||||||
// rather than in the Building instance, so placing/removing them must register or
|
// rather than in the Building instance, so placing/removing them must register or
|
||||||
// unregister a tile with BeltSystem.
|
// unregister a tile with BeltSystem.
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "AiSystem.h"
|
#include "AiSystem.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ AiSystem::AiSystem(const GameConfig& config)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
void AiSystem::tick(EntityAdmin& admin, const FactoryState& state,
|
||||||
const DebrisSystem& debris)
|
const DebrisSystem& debris)
|
||||||
{
|
{
|
||||||
TRACE();
|
TRACE();
|
||||||
@@ -55,7 +56,7 @@ void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
|||||||
m_attackEvaluator.evaluate(admin);
|
m_attackEvaluator.evaluate(admin);
|
||||||
m_repairEvaluator.evaluate(admin);
|
m_repairEvaluator.evaluate(admin);
|
||||||
m_salvageScrapEvaluator.evaluate(admin, debris);
|
m_salvageScrapEvaluator.evaluate(admin, debris);
|
||||||
m_deliverScrapEvaluator.evaluate(admin, buildings);
|
m_deliverScrapEvaluator.evaluate(admin, state);
|
||||||
|
|
||||||
// Phase 2: pick the highest-scoring behavior per ship.
|
// Phase 2: pick the highest-scoring behavior per ship.
|
||||||
selectWinningBehaviors(admin);
|
selectWinningBehaviors(admin);
|
||||||
@@ -68,7 +69,7 @@ void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
|||||||
m_attackExecutor.execute(admin);
|
m_attackExecutor.execute(admin);
|
||||||
m_repairExecutor.execute(admin);
|
m_repairExecutor.execute(admin);
|
||||||
m_salvageScrapExecutor.execute(admin);
|
m_salvageScrapExecutor.execute(admin);
|
||||||
m_deliverScrapExecutor.execute(admin, buildings);
|
m_deliverScrapExecutor.execute(admin, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiSystem::selectWinningBehaviors(EntityAdmin& admin)
|
void AiSystem::selectWinningBehaviors(EntityAdmin& admin)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include "AdvanceEvaluator.h"
|
#include "AdvanceEvaluator.h"
|
||||||
#include "AdvanceExecutor.h"
|
#include "AdvanceExecutor.h"
|
||||||
#include "AttackEvaluator.h"
|
#include "AttackEvaluator.h"
|
||||||
@@ -17,7 +19,6 @@
|
|||||||
#include "StandbyEvaluator.h"
|
#include "StandbyEvaluator.h"
|
||||||
#include "StandbyExecutor.h"
|
#include "StandbyExecutor.h"
|
||||||
|
|
||||||
class BuildingSystem;
|
|
||||||
class EntityAdmin;
|
class EntityAdmin;
|
||||||
class DebrisSystem;
|
class DebrisSystem;
|
||||||
struct GameConfig;
|
struct GameConfig;
|
||||||
@@ -34,7 +35,7 @@ class AiSystem
|
|||||||
public:
|
public:
|
||||||
explicit AiSystem(const GameConfig& config);
|
explicit AiSystem(const GameConfig& config);
|
||||||
|
|
||||||
void tick(EntityAdmin& admin, const BuildingSystem& buildings, const DebrisSystem& debris);
|
void tick(EntityAdmin& admin, const FactoryState& state, const DebrisSystem& debris);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void selectWinningBehaviors(EntityAdmin& admin);
|
void selectWinningBehaviors(EntityAdmin& admin);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "SalvagerSystem.h"
|
#include "SalvagerSystem.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ SalvagerSystem::SalvagerSystem(EntityAdmin& admin)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SalvagerSystem::tick(Tick currentTick, DebrisSystem& debris, BuildingSystem& buildings,
|
void SalvagerSystem::tick(Tick currentTick, DebrisSystem& debris, FactoryState& state,
|
||||||
std::vector<BeamFiredEvent>& outBeamFiredEvents)
|
std::vector<BeamFiredEvent>& outBeamFiredEvents)
|
||||||
{
|
{
|
||||||
TRACE();
|
TRACE();
|
||||||
@@ -89,7 +90,7 @@ void SalvagerSystem::tick(Tick currentTick, DebrisSystem& debris, BuildingSystem
|
|||||||
[&](entt::entity ship, const DeliverScrapBehavior& deliver, const PositionComponent& pos)
|
[&](entt::entity ship, const DeliverScrapBehavior& deliver, const PositionComponent& pos)
|
||||||
{
|
{
|
||||||
if (!deliver.deliveryBay.has_value()) { return; }
|
if (!deliver.deliveryBay.has_value()) { return; }
|
||||||
const Building* bay = buildings.findBuilding(*deliver.deliveryBay);
|
const Building* bay = findBuilding(state, *deliver.deliveryBay);
|
||||||
if (!bay) { return; }
|
if (!bay) { return; }
|
||||||
|
|
||||||
const QVector2D bayCenter(bay->anchor.x() + bay->footprint.width() / 2.0f,
|
const QVector2D bayCenter(bay->anchor.x() + bay->footprint.width() / 2.0f,
|
||||||
@@ -100,7 +101,7 @@ void SalvagerSystem::tick(Tick currentTick, DebrisSystem& debris, BuildingSystem
|
|||||||
if (!m_admin.hasAll<CargoComponent>(ship)) { return; }
|
if (!m_admin.hasAll<CargoComponent>(ship)) { return; }
|
||||||
CargoComponent& cargo = m_admin.get<CargoComponent>(ship);
|
CargoComponent& cargo = m_admin.get<CargoComponent>(ship);
|
||||||
if (cargo.current <= 0) { return; }
|
if (cargo.current <= 0) { return; }
|
||||||
if (buildings.deliverScrapToSalvageBay(*deliver.deliveryBay))
|
if (deliverScrapToSalvageBay(state, *deliver.deliveryBay))
|
||||||
{
|
{
|
||||||
--cargo.current;
|
--cargo.current;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "BeamFiredEvent.h"
|
#include "BeamFiredEvent.h"
|
||||||
@@ -7,7 +9,6 @@
|
|||||||
|
|
||||||
#include "entt/entity/entity.hpp"
|
#include "entt/entity/entity.hpp"
|
||||||
|
|
||||||
class BuildingSystem;
|
|
||||||
class EntityAdmin;
|
class EntityAdmin;
|
||||||
class DebrisSystem;
|
class DebrisSystem;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ class SalvagerSystem
|
|||||||
public:
|
public:
|
||||||
explicit SalvagerSystem(EntityAdmin& admin);
|
explicit SalvagerSystem(EntityAdmin& admin);
|
||||||
|
|
||||||
void tick(Tick currentTick, DebrisSystem& debris, BuildingSystem& buildings,
|
void tick(Tick currentTick, DebrisSystem& debris, FactoryState& state,
|
||||||
std::vector<BeamFiredEvent>& outBeamFiredEvents);
|
std::vector<BeamFiredEvent>& outBeamFiredEvents);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "DeliverScrapEvaluator.h"
|
#include "DeliverScrapEvaluator.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
#include "PositionComponent.h"
|
#include "PositionComponent.h"
|
||||||
#include "tracing.h"
|
#include "tracing.h"
|
||||||
|
|
||||||
void DeliverScrapEvaluator::evaluate(EntityAdmin& admin, const BuildingSystem& buildings)
|
void DeliverScrapEvaluator::evaluate(EntityAdmin& admin, const FactoryState& state)
|
||||||
{
|
{
|
||||||
TRACE();
|
TRACE();
|
||||||
const std::unordered_map<entt::entity, CargoState> cargoByShip = buildCargoByShip(admin);
|
const std::unordered_map<entt::entity, CargoState> cargoByShip = buildCargoByShip(admin);
|
||||||
@@ -34,7 +35,7 @@ void DeliverScrapEvaluator::evaluate(EntityAdmin& admin, const BuildingSystem& b
|
|||||||
if (!deliver.deliveryBay.has_value())
|
if (!deliver.deliveryBay.has_value())
|
||||||
{
|
{
|
||||||
const Building* bay =
|
const Building* bay =
|
||||||
buildings.findNearestBuilding(pos.value, BuildingType::SalvageBay);
|
findNearestBuilding(state, pos.value, BuildingType::SalvageBay);
|
||||||
if (bay) { deliver.deliveryBay = bay->id; }
|
if (bay) { deliver.deliveryBay = bay->id; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
class EntityAdmin;
|
class EntityAdmin;
|
||||||
class BuildingSystem;
|
|
||||||
|
|
||||||
// Scores high only when the ship's cargo is full, and assigns the nearest
|
// Scores high only when the ship's cargo is full, and assigns the nearest
|
||||||
// SalvageBay as the delivery destination.
|
// SalvageBay as the delivery destination.
|
||||||
class DeliverScrapEvaluator
|
class DeliverScrapEvaluator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void evaluate(EntityAdmin& admin, const BuildingSystem& buildings);
|
void evaluate(EntityAdmin& admin, const FactoryState& state);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "DeliverScrapExecutor.h"
|
#include "DeliverScrapExecutor.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
#include "SelectedBehaviorComponent.h"
|
#include "SelectedBehaviorComponent.h"
|
||||||
#include "tracing.h"
|
#include "tracing.h"
|
||||||
|
|
||||||
void DeliverScrapExecutor::execute(EntityAdmin& admin, const BuildingSystem& buildings)
|
void DeliverScrapExecutor::execute(EntityAdmin& admin, const FactoryState& state)
|
||||||
{
|
{
|
||||||
TRACE();
|
TRACE();
|
||||||
admin.forEach<DeliverScrapBehavior, SelectedBehaviorComponent, PositionComponent,
|
admin.forEach<DeliverScrapBehavior, SelectedBehaviorComponent, PositionComponent,
|
||||||
@@ -26,7 +27,7 @@ void DeliverScrapExecutor::execute(EntityAdmin& admin, const BuildingSystem& bui
|
|||||||
QVector2D dest = pos.value;
|
QVector2D dest = pos.value;
|
||||||
if (deliver.deliveryBay.has_value())
|
if (deliver.deliveryBay.has_value())
|
||||||
{
|
{
|
||||||
const Building* bay = buildings.findBuilding(*deliver.deliveryBay);
|
const Building* bay = findBuilding(state, *deliver.deliveryBay);
|
||||||
if (bay)
|
if (bay)
|
||||||
{
|
{
|
||||||
dest = QVector2D(bay->anchor.x() + bay->footprint.width() / 2.0f,
|
dest = QVector2D(bay->anchor.x() + bay->footprint.width() / 2.0f,
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
class EntityAdmin;
|
class EntityAdmin;
|
||||||
class BuildingSystem;
|
|
||||||
|
|
||||||
// Moves a ship toward its delivery bay when DeliverScrap is the winning
|
// Moves a ship toward its delivery bay when DeliverScrap is the winning
|
||||||
// behavior. Never decrements cargo — SalvagerSystem performs the delivery.
|
// behavior. Never decrements cargo — SalvagerSystem performs the delivery.
|
||||||
class DeliverScrapExecutor
|
class DeliverScrapExecutor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void execute(EntityAdmin& admin, const BuildingSystem& buildings);
|
void execute(EntityAdmin& admin, const FactoryState& state);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include "FactoryQueries.h"
|
||||||
#include "PortGeometry.h"
|
#include "PortGeometry.h"
|
||||||
#include "StateChecksum.h"
|
#include "StateChecksum.h"
|
||||||
#include "SurfaceMask.h"
|
#include "SurfaceMask.h"
|
||||||
@@ -1264,87 +1265,37 @@ void BuildingSystem::forEachIncomingItem(
|
|||||||
|
|
||||||
const Building* BuildingSystem::findBuilding(BuildingId id) const
|
const Building* BuildingSystem::findBuilding(BuildingId id) const
|
||||||
{
|
{
|
||||||
for (const Building& building : m_state.buildings)
|
return ::findBuilding(m_state, id);
|
||||||
{
|
|
||||||
if (building.id == id)
|
|
||||||
{
|
|
||||||
return &building;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Building* BuildingSystem::findBuildingMutable(BuildingId id)
|
Building* BuildingSystem::findBuildingMutable(BuildingId id)
|
||||||
{
|
{
|
||||||
for (Building& building : m_state.buildings)
|
return ::findBuilding(m_state, id);
|
||||||
{
|
|
||||||
if (building.id == id)
|
|
||||||
{
|
|
||||||
return &building;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConstructionSite* BuildingSystem::findSite(BuildingId id) const
|
const ConstructionSite* BuildingSystem::findSite(BuildingId id) const
|
||||||
{
|
{
|
||||||
for (const ConstructionSite& site : m_state.constructionQueue)
|
return ::findSite(m_state, id);
|
||||||
{
|
|
||||||
if (site.id == id)
|
|
||||||
{
|
|
||||||
return &site;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Building> BuildingSystem::getAllBuildings() const
|
std::vector<Building> BuildingSystem::getAllBuildings() const
|
||||||
{
|
{
|
||||||
return m_state.buildings;
|
return ::getAllBuildings(m_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ConstructionSite> BuildingSystem::getAllSites() const
|
std::vector<ConstructionSite> BuildingSystem::getAllSites() const
|
||||||
{
|
{
|
||||||
return std::vector<ConstructionSite>(m_state.constructionQueue.begin(),
|
return ::getAllSites(m_state);
|
||||||
m_state.constructionQueue.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
bool isProductionBuildingType(BuildingType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case BuildingType::Miner:
|
|
||||||
case BuildingType::Smelter:
|
|
||||||
case BuildingType::Assembler:
|
|
||||||
case BuildingType::ReprocessingPlant:
|
|
||||||
case BuildingType::Shipyard:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int BuildingSystem::getProductionBuildingCount() const
|
int BuildingSystem::getProductionBuildingCount() const
|
||||||
{
|
{
|
||||||
int count = 0;
|
return ::getProductionBuildingCount(m_state);
|
||||||
for (const Building& b : m_state.buildings)
|
|
||||||
{
|
|
||||||
if (isProductionBuildingType(b.type)) { ++count; }
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int BuildingSystem::getActiveProductionBuildingCount() const
|
int BuildingSystem::getActiveProductionBuildingCount() const
|
||||||
{
|
{
|
||||||
int count = 0;
|
return ::getActiveProductionBuildingCount(m_state);
|
||||||
for (const Building& b : m_state.buildings)
|
|
||||||
{
|
|
||||||
if (isProductionBuildingType(b.type) && b.production.has_value()) { ++count; }
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const RecipeDef*>
|
std::vector<const RecipeDef*>
|
||||||
@@ -1522,7 +1473,7 @@ std::vector<BuildingSystem::BeltTileInfo> BuildingSystem::getAllBeltTiles() cons
|
|||||||
|
|
||||||
bool BuildingSystem::isTileOccupied(QPoint tile) const
|
bool BuildingSystem::isTileOccupied(QPoint tile) const
|
||||||
{
|
{
|
||||||
return m_state.grid.isOccupied(tile);
|
return ::isTileOccupied(m_state, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
|
std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
|
||||||
@@ -1643,53 +1594,12 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation)
|
|||||||
const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
|
const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
|
||||||
BuildingType type) const
|
BuildingType type) const
|
||||||
{
|
{
|
||||||
const Building* best = nullptr;
|
return ::findNearestBuilding(m_state, worldPos, type);
|
||||||
float bestDist = std::numeric_limits<float>::max();
|
|
||||||
for (const Building& b : m_state.buildings)
|
|
||||||
{
|
|
||||||
if (b.type != type)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QVector2D center(b.anchor.x() + b.footprint.width() / 2.0f,
|
|
||||||
b.anchor.y() + b.footprint.height() / 2.0f);
|
|
||||||
float dist = (center - worldPos).length();
|
|
||||||
if (dist < bestDist)
|
|
||||||
{
|
|
||||||
bestDist = dist;
|
|
||||||
best = &b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId)
|
bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId)
|
||||||
{
|
{
|
||||||
Building* bay = nullptr;
|
return ::deliverScrapToSalvageBay(m_state, bayId);
|
||||||
for (Building& b : m_state.buildings)
|
|
||||||
{
|
|
||||||
if (b.id == bayId)
|
|
||||||
{
|
|
||||||
bay = &b;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!bay || bay->type != BuildingType::SalvageBay)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (bay->queuedForDeconstruction)
|
|
||||||
{
|
|
||||||
return false; // queued for deconstruction: stopped operating (REQ-BLD-DECON-QUEUE)
|
|
||||||
}
|
|
||||||
// Emerging scrap still counts against the bay's holding capacity
|
|
||||||
// (REQ-MAT-OUTPUT-EMERGE).
|
|
||||||
if (bay->getOutputItemCount() >= bay->outputBuffer.capacity)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bay->outputBuffer.items.push_back(Item{ItemType{"scrap"}});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildingId BuildingSystem::placeImmediate(BuildingType type,
|
BuildingId BuildingSystem::placeImmediate(BuildingType type,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ SET(HDRS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/FactoryState.h
|
${CMAKE_CURRENT_SOURCE_DIR}/FactoryState.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/FactoryQueries.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.h
|
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayout.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayout.h
|
||||||
@@ -39,6 +40,7 @@ SET(SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/BeltSystem.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/BeltSystem.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/BuildingConfig.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/BuildingGrid.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/FactoryQueries.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipStatsCalculator.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/ShipStatsCalculator.cpp
|
||||||
|
|||||||
122
src/lib/sim/FactoryQueries.cpp
Normal file
122
src/lib/sim/FactoryQueries.cpp
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "Item.h"
|
||||||
|
#include "ItemType.h"
|
||||||
|
|
||||||
|
const Building* findBuilding(const FactoryState& state, BuildingId id)
|
||||||
|
{
|
||||||
|
for (const Building& building : state.buildings)
|
||||||
|
{
|
||||||
|
if (building.id == id)
|
||||||
|
{
|
||||||
|
return &building;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Building* findBuilding(FactoryState& state, BuildingId id)
|
||||||
|
{
|
||||||
|
for (Building& building : state.buildings)
|
||||||
|
{
|
||||||
|
if (building.id == id)
|
||||||
|
{
|
||||||
|
return &building;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConstructionSite* findSite(const FactoryState& state, BuildingId id)
|
||||||
|
{
|
||||||
|
for (const ConstructionSite& site : state.constructionQueue)
|
||||||
|
{
|
||||||
|
if (site.id == id)
|
||||||
|
{
|
||||||
|
return &site;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Building> getAllBuildings(const FactoryState& state)
|
||||||
|
{
|
||||||
|
return state.buildings;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ConstructionSite> getAllSites(const FactoryState& state)
|
||||||
|
{
|
||||||
|
return std::vector<ConstructionSite>(state.constructionQueue.begin(),
|
||||||
|
state.constructionQueue.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
int getProductionBuildingCount(const FactoryState& state)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (const Building& b : state.buildings)
|
||||||
|
{
|
||||||
|
if (isProductionBuildingType(b.type)) { ++count; }
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getActiveProductionBuildingCount(const FactoryState& state)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (const Building& b : state.buildings)
|
||||||
|
{
|
||||||
|
if (isProductionBuildingType(b.type) && b.production.has_value()) { ++count; }
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTileOccupied(const FactoryState& state, QPoint tile)
|
||||||
|
{
|
||||||
|
return state.grid.isOccupied(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
|
||||||
|
BuildingType type)
|
||||||
|
{
|
||||||
|
const Building* best = nullptr;
|
||||||
|
float bestDist = std::numeric_limits<float>::max();
|
||||||
|
for (const Building& b : state.buildings)
|
||||||
|
{
|
||||||
|
if (b.type != type)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QVector2D center(b.anchor.x() + b.footprint.width() / 2.0f,
|
||||||
|
b.anchor.y() + b.footprint.height() / 2.0f);
|
||||||
|
float dist = (center - worldPos).length();
|
||||||
|
if (dist < bestDist)
|
||||||
|
{
|
||||||
|
bestDist = dist;
|
||||||
|
best = &b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deliverScrapToSalvageBay(FactoryState& state, BuildingId bayId)
|
||||||
|
{
|
||||||
|
Building* bay = findBuilding(state, bayId);
|
||||||
|
if (!bay || bay->type != BuildingType::SalvageBay)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bay->queuedForDeconstruction)
|
||||||
|
{
|
||||||
|
return false; // queued for deconstruction: stopped operating (REQ-BLD-DECON-QUEUE)
|
||||||
|
}
|
||||||
|
// Emerging scrap still counts against the bay's holding capacity
|
||||||
|
// (REQ-MAT-OUTPUT-EMERGE).
|
||||||
|
if (bay->getOutputItemCount() >= bay->outputBuffer.capacity)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bay->outputBuffer.items.push_back(Item{ItemType{"scrap"}});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
51
src/lib/sim/FactoryQueries.h
Normal file
51
src/lib/sim/FactoryQueries.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QVector2D>
|
||||||
|
|
||||||
|
#include "Building.h"
|
||||||
|
#include "BuildingId.h"
|
||||||
|
#include "BuildingType.h"
|
||||||
|
#include "FactoryState.h"
|
||||||
|
|
||||||
|
// Queries and operations over the factory's world data that need nothing but that
|
||||||
|
// data — no config, no belts, no RNG. Free functions rather than BuildingSystem
|
||||||
|
// methods so that callers depend on the data they read instead of on the system
|
||||||
|
// that happens to tick it (see FactoryState.h).
|
||||||
|
//
|
||||||
|
// A helper belongs here only if it is a pure function of FactoryState. Anything
|
||||||
|
// needing GameConfig or the asteroid bound stays on BuildingSystem for now.
|
||||||
|
|
||||||
|
// The building with the given id, or nullptr when no building has it. Construction
|
||||||
|
// sites are not buildings yet — use findSite for those.
|
||||||
|
const Building* findBuilding(const FactoryState& state, BuildingId id);
|
||||||
|
Building* findBuilding(FactoryState& state, BuildingId id);
|
||||||
|
|
||||||
|
// The queued construction site with the given id, or nullptr.
|
||||||
|
const ConstructionSite* findSite(const FactoryState& state, BuildingId id);
|
||||||
|
|
||||||
|
std::vector<Building> getAllBuildings(const FactoryState& state);
|
||||||
|
std::vector<ConstructionSite> getAllSites(const FactoryState& state);
|
||||||
|
|
||||||
|
// REQ-UI-DEBUG-OVERLAY "Max Factory Production": count of completed
|
||||||
|
// (operational) Miner/Smelter/Assembler/ReprocessingPlant/Shipyard buildings.
|
||||||
|
int getProductionBuildingCount(const FactoryState& state);
|
||||||
|
|
||||||
|
// REQ-UI-DEBUG-OVERLAY "Current Factory Production": subset of the above that
|
||||||
|
// currently has an active production cycle.
|
||||||
|
int getActiveProductionBuildingCount(const FactoryState& state);
|
||||||
|
|
||||||
|
bool isTileOccupied(const FactoryState& state, QPoint tile);
|
||||||
|
|
||||||
|
// The nearest building of the given type to a world position, or nullptr when
|
||||||
|
// none exists. Distance is measured to the building's footprint centre.
|
||||||
|
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
|
||||||
|
BuildingType type);
|
||||||
|
|
||||||
|
// Hands one scrap to a Salvage Bay's output buffer (REQ-BLD-SALVAGE-BAY). Fails
|
||||||
|
// if the id is not a Salvage Bay, it is queued for deconstruction
|
||||||
|
// (REQ-BLD-DECON-QUEUE), or its holding capacity is already taken — emerging
|
||||||
|
// scrap counts against that capacity (REQ-MAT-OUTPUT-EMERGE).
|
||||||
|
bool deliverScrapToSalvageBay(FactoryState& state, BuildingId bayId);
|
||||||
@@ -260,10 +260,10 @@ void Simulation::tick()
|
|||||||
m_shipSystem->clearMovementIntents();
|
m_shipSystem->clearMovementIntents();
|
||||||
// Score-based behavior selection: evaluate, select winner, execute (sets
|
// Score-based behavior selection: evaluate, select winner, execute (sets
|
||||||
// movement intent + preferred module targets only — no world mutation).
|
// movement intent + preferred module targets only — no world mutation).
|
||||||
m_aiSystem->tick(m_admin, *m_buildingSystem, *m_debrisSystem);
|
m_aiSystem->tick(m_admin, m_factoryState, *m_debrisSystem);
|
||||||
// Module systems perform the world mutation (collection/delivery, healing).
|
// Module systems perform the world mutation (collection/delivery, healing).
|
||||||
// Each emits its tool beams and applies its own delayed (mid-beam) effects.
|
// Each emits its tool beams and applies its own delayed (mid-beam) effects.
|
||||||
m_salvagerSystem->tick(m_currentTick, *m_debrisSystem, *m_buildingSystem, m_beamFiredEvents);
|
m_salvagerSystem->tick(m_currentTick, *m_debrisSystem, m_factoryState, m_beamFiredEvents);
|
||||||
m_repairSystem->tick(m_currentTick, m_beamFiredEvents);
|
m_repairSystem->tick(m_currentTick, m_beamFiredEvents);
|
||||||
|
|
||||||
// Step 8: combat resolution
|
// Step 8: combat resolution
|
||||||
|
|||||||
@@ -97,14 +97,14 @@ struct Fixture
|
|||||||
void decide()
|
void decide()
|
||||||
{
|
{
|
||||||
ships.clearMovementIntents();
|
ships.clearMovementIntents();
|
||||||
ai.tick(admin, buildings, scraps);
|
ai.tick(admin, state, scraps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// World mutation: collection/delivery and healing.
|
// World mutation: collection/delivery and healing.
|
||||||
void runModules()
|
void runModules()
|
||||||
{
|
{
|
||||||
beamEvents.clear();
|
beamEvents.clear();
|
||||||
salvager.tick(tick, scraps, buildings, beamEvents);
|
salvager.tick(tick, scraps, state, beamEvents);
|
||||||
repair.tick(tick, beamEvents);
|
repair.tick(tick, beamEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ struct Fixture
|
|||||||
void salvageTick()
|
void salvageTick()
|
||||||
{
|
{
|
||||||
beamEvents.clear();
|
beamEvents.clear();
|
||||||
salvager.tick(tick, scraps, buildings, beamEvents);
|
salvager.tick(tick, scraps, state, beamEvents);
|
||||||
++tick;
|
++tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user