#include "SelectionContentFactory.h" #include "BeltContent.h" #include "DebrisContent.h" #include "FactoryQueries.h" #include "FieldMultiContent.h" #include "HqContent.h" #include "MultiBuildingContent.h" #include "RecipeProductionContent.h" #include "ShipContent.h" #include "ShipIdentityComponent.h" #include "ShipyardContent.h" #include "SplitterContent.h" #include "StationBodyComponent.h" #include "StationContent.h" #include "StorageContent.h" #include "Simulation.h" namespace { // The catalog row a single building type belongs to (REQ-UI-SELECTION-CONTENT). SelectionContentKind getKindForType(BuildingType type) { switch (type) { case BuildingType::Miner: case BuildingType::Assembler: case BuildingType::Smelter: case BuildingType::ReprocessingPlant: return SelectionContentKind::RecipeProduction; case BuildingType::Shipyard: return SelectionContentKind::Shipyard; case BuildingType::SalvageBay: return SelectionContentKind::Storage; case BuildingType::Hq: return SelectionContentKind::Hq; case BuildingType::Belt: case BuildingType::TunnelEntry: case BuildingType::TunnelExit: return SelectionContentKind::Belt; case BuildingType::Splitter: return SelectionContentKind::Splitter; case BuildingType::PlayerDefenceStation: case BuildingType::EnemyDefenceStation: // Defence stations are field objects backed by entities; these two enum // values exist only for cost and visuals lookup and never reach the panel as // a building selection. The count summary is the harmless fallback. return SelectionContentKind::MultiBuilding; } return SelectionContentKind::MultiBuilding; } ContentKey chooseBuildingContent(const SelectionRequest& request, Simulation& sim) { const FactoryState& state = sim.getFactoryState(); if (request.buildings.size() == 1) { const BuildingId id = request.buildings.front(); const Building* building = findBuilding(state, id); if (building) { return { getKindForType(building->type), false }; } const ConstructionSite* site = findSite(state, id); if (site) { return { getKindForType(site->type), true }; } // The building went away under the panel; the selection is stale and there is // nothing to show (REQ-UI-EMPTY-SELECTION). return {}; } // Several buildings aggregate into one card only when every part of that card's // runtime content aggregates (REQ-UI-SELECTION-AGGREGATE). Belt-subsystem tiles do: // the item list and the clear action already read and act on the whole selection // (REQ-UI-BELT-ITEMS, REQ-UI-BELT-CLEAR). Splitters are among them -- their output // filters are per-object configuration, which an aggregate simply does not show. // // Construction sites are excluded: findBuilding fails for a site, whose card is its // own construction progress and whose tile the belt subsystem does not know yet. bool allBeltTiles = true; for (BuildingId id : request.buildings) { const Building* building = findBuilding(state, id); if (!building || !isBeltSubsystemType(building->type)) { allBeltTiles = false; break; } } if (allBeltTiles) { return { SelectionContentKind::Belt, false }; } return { SelectionContentKind::MultiBuilding, false }; } ContentKey chooseFieldContent(const SelectionRequest& request, Simulation& sim) { // A full single-object card is shown for a lone actor, and for debris whether one // piece or several -- debris is the field category's other aggregating content // (REQ-UI-FIELD-MULTI-SELECTION, REQ-UI-SELECTION-AGGREGATE). if (request.actors.size() == 1 && request.debris.empty()) { EntityAdmin& admin = sim.getAdmin(); const entt::entity actor = request.actors.front(); if (admin.isValid(actor) && admin.hasAll(actor)) { return { SelectionContentKind::Ship, false }; } if (admin.isValid(actor) && admin.hasAll(actor)) { return { SelectionContentKind::Station, false }; } return {}; } if (request.actors.empty() && !request.debris.empty()) { return { SelectionContentKind::Debris, false }; } return { SelectionContentKind::FieldMulti, false }; } } // namespace ContentKey chooseContent(const SelectionRequest& request, Simulation& sim) { if (request.isEmpty()) { return {}; } // Buildings win, so a non-empty building selection is the whole selection // (REQ-UI-SELECTION-CATEGORIES). if (!request.buildings.empty()) { return chooseBuildingContent(request, sim); } return chooseFieldContent(request, sim); } SelectionContent* createContent(const ContentKey& key, const SelectionRequest& request, const SelectionContext& context, QWidget* parent) { switch (key.kind) { case SelectionContentKind::None: return nullptr; case SelectionContentKind::RecipeProduction: return new RecipeProductionContent(context, request, parent); case SelectionContentKind::Shipyard: return new ShipyardContent(context, request, parent); case SelectionContentKind::Storage: return new StorageContent(context, request, parent); case SelectionContentKind::Hq: return new HqContent(context, request, parent); case SelectionContentKind::Belt: return new BeltContent(context, request, parent); case SelectionContentKind::Splitter: return new SplitterContent(context, request, parent); case SelectionContentKind::MultiBuilding: return new MultiBuildingContent(context, request, parent); case SelectionContentKind::Ship: return new ShipContent(context, request, parent); case SelectionContentKind::Station: return new StationContent(context, request, parent); case SelectionContentKind::Debris: return new DebrisContent(context, request, parent); case SelectionContentKind::FieldMulti: return new FieldMultiContent(context, request, parent); } return nullptr; }