Files
dota_factory/src/ui/selection/SelectionContentFactory.cpp

188 lines
6.7 KiB
C++

#include "SelectionContentFactory.h"
#include "AutoProductionContent.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:
return SelectionContentKind::RecipeProduction;
case BuildingType::Smelter:
case BuildingType::ReprocessingPlant:
return SelectionContentKind::AutoProduction;
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;
}
// A belt, tunnel entry or tunnel exit -- the types whose card is the clear action alone
// and therefore aggregates (REQ-UI-SELECTION-AGGREGATE). The splitter is deliberately
// excluded: it carries per-object output filters, which have no aggregate.
bool isAggregatableBeltType(BuildingType type)
{
return type == BuildingType::Belt
|| type == BuildingType::TunnelEntry
|| type == BuildingType::TunnelExit;
}
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
// aggregates (REQ-UI-SELECTION-AGGREGATE). Construction sites are excluded from the
// belt case: a site's card is its own construction progress, which several sites
// cannot share.
bool allAggregatableBelts = true;
for (BuildingId id : request.buildings)
{
const Building* building = findBuilding(state, id);
if (!building || !isAggregatableBeltType(building->type))
{
allAggregatableBelts = false;
break;
}
}
if (allAggregatableBelts)
{
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<ShipIdentityComponent>(actor))
{
return { SelectionContentKind::Ship, false };
}
if (admin.isValid(actor) && admin.hasAll<StationBodyComponent>(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::AutoProduction:
return new AutoProductionContent(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;
}