move blueprints out of the sidebar into Ctrl+C / Ctrl+V dialogs

This commit is contained in:
2026-08-06 19:03:59 +02:00
parent 2cbcf1554f
commit 18cfe238f6
22 changed files with 1021 additions and 400 deletions

View File

@@ -3,10 +3,14 @@
#include <algorithm>
#include <climits>
#include <cstddef>
#include <map>
#include "BeltSystem.h"
#include "Building.h"
#include "BuildingsConfig.h"
#include "BuildingSystem.h"
#include "DisplayName.h"
#include "Simulation.h"
namespace
@@ -49,6 +53,21 @@ std::optional<SelectedBuilding> resolvePlaceable(const Simulation& sim, Building
resolved.bodyCells = building ? &building->bodyCells : &site->bodyCells;
return resolved;
}
// Position of a building type in buildings.toml, which is the order the build button
// bar lays out its buttons (REQ-UI-BUILD-BAR) and so the tie-break order for a
// blueprint's contents line. A type with no config entry sorts after every known one.
std::size_t configOrderIndex(BuildingType type, const BuildingsConfig& buildings)
{
for (std::size_t i = 0; i < buildings.buildings.size(); ++i)
{
if (buildings.buildings[i].type == type)
{
return i;
}
}
return buildings.buildings.size();
}
} // namespace
std::optional<BuildingConfig> readBuildingConfig(const Simulation& sim, BuildingId id)
@@ -166,3 +185,56 @@ bool selectionHasPlaceableBuilding(const Simulation& sim,
}
return false;
}
std::vector<BlueprintContentEntry> summarizeBlueprintContents(
const Blueprint& blueprint, const BuildingsConfig& buildings)
{
std::map<BuildingType, int> counts;
for (const BlueprintBuilding& building : blueprint.buildings)
{
++counts[building.type];
}
// The config index is carried through the sort so the comparator stays a strict
// total order; the enum value is the final tie-break, which only two config-less
// types could ever reach.
struct RankedType
{
BuildingType type;
int count;
std::size_t order;
};
std::vector<RankedType> ranked;
ranked.reserve(counts.size());
for (const std::pair<const BuildingType, int>& entry : counts)
{
ranked.push_back({entry.first, entry.second,
configOrderIndex(entry.first, buildings)});
}
std::sort(ranked.begin(), ranked.end(),
[](const RankedType& left, const RankedType& right)
{
if (left.count != right.count) { return left.count > right.count; }
if (left.order != right.order) { return left.order < right.order; }
return static_cast<int>(left.type) < static_cast<int>(right.type);
});
std::vector<BlueprintContentEntry> summary;
summary.reserve(ranked.size());
for (const RankedType& entry : ranked)
{
summary.push_back({toDisplayName(buildingTypeId(entry.type)), entry.count});
}
return summary;
}
int computeBlueprintCost(const Blueprint& blueprint, const BuildingsConfig& buildings)
{
int total = 0;
for (const BlueprintBuilding& building : blueprint.buildings)
{
const BuildingDef* def = buildings.findBuildingDef(building.type);
if (def) { total += def->cost; }
}
return total;
}