Add building status light

This commit is contained in:
2026-07-20 22:19:58 +02:00
parent c7ecce6ac4
commit 1cbc695bc5
8 changed files with 385 additions and 57 deletions

View File

@@ -1308,3 +1308,116 @@ TEST_CASE("BuildingSystem: splitter filters configured on a construction site ca
REQUIRE(builtInfo->filterA == filterA);
REQUIRE(builtInfo->filterB.empty());
}
// ---------------------------------------------------------------------------
// Production status classifier (REQ-UI-STATUS-LIGHT)
// ---------------------------------------------------------------------------
TEST_CASE("BuildingSystem: getProductionStatus classifies production state", "[building]")
{
PlacementFixture f;
// Pick representative config ids so the test survives content edits.
std::string minerRecipeId;
for (const RecipeDef& r : f.cfg.recipes.recipes)
{
if (r.building == BuildingType::Miner) { minerRecipeId = r.id; break; }
}
REQUIRE_FALSE(minerRecipeId.empty());
const RecipeDef* assemblerRecipe = nullptr;
for (const RecipeDef& r : f.cfg.recipes.recipes)
{
if (r.building == BuildingType::Assembler && !r.inputs.empty())
{
assemblerRecipe = &r;
break;
}
}
REQUIRE(assemblerRecipe != nullptr);
std::string shipId;
for (const ShipDef& s : f.cfg.ships.ships)
{
if (!s.schematic.materials.empty()) { shipId = s.id; break; }
}
REQUIRE_FALSE(shipId.empty());
const auto statusOf = [&f](const Building& b) { return f.bs.getProductionStatus(b); };
SECTION("non-production buildings show no status light")
{
Building belt; belt.type = BuildingType::Belt;
Building hq; hq.type = BuildingType::Hq;
REQUIRE_FALSE(statusOf(belt).has_value());
REQUIRE_FALSE(statusOf(hq).has_value());
}
SECTION("Miner: unconfigured, producing, output-blocked")
{
Building miner; miner.type = BuildingType::Miner;
REQUIRE(statusOf(miner) == ProductionStatus::Unconfigured); // no recipe -> grey
miner.recipeId = minerRecipeId;
miner.production = Production{};
REQUIRE(statusOf(miner) == ProductionStatus::Producing); // active cycle -> green
// A miner has no inputs, so its only idle reason is a full output buffer.
miner.production = std::nullopt;
miner.outputBuffer.capacity = 2;
miner.outputBuffer.items = { makeItem("iron_ore"), makeItem("iron_ore") };
REQUIRE(statusOf(miner) == ProductionStatus::Blocked); // -> yellow
}
SECTION("Assembler: starved vs blocked, input-missing takes precedence")
{
Building assembler; assembler.type = BuildingType::Assembler;
assembler.recipeId = assemblerRecipe->id;
// Idle with inputs missing -> red.
REQUIRE(statusOf(assembler) == ProductionStatus::Starved);
// Inputs present but idle -> the only remaining reason is a full output
// buffer -> yellow.
for (const RecipeIngredient& ing : assemblerRecipe->inputs)
{
assembler.inputBuffer.counts[ItemType{ing.item}] = ing.amount;
}
REQUIRE(statusOf(assembler) == ProductionStatus::Blocked);
// Inputs missing AND output full -> red wins over yellow.
assembler.inputBuffer.counts.clear();
assembler.outputBuffer.capacity = 2;
assembler.outputBuffer.items = { makeItem("x"), makeItem("x") };
REQUIRE(statusOf(assembler) == ProductionStatus::Starved);
}
SECTION("Smelter (auto-recipe) is never grey")
{
Building smelter; smelter.type = BuildingType::Smelter;
// No player-selectable recipe and empty inputs -> red, not grey.
REQUIRE(statusOf(smelter) == ProductionStatus::Starved);
}
SECTION("Shipyard: unconfigured, then starved without materials, then producing")
{
Building yard; yard.type = BuildingType::Shipyard;
REQUIRE(statusOf(yard) == ProductionStatus::Unconfigured); // no schematic -> grey
yard.recipeId = shipId;
REQUIRE(statusOf(yard) == ProductionStatus::Starved); // no materials -> red
yard.production = Production{};
REQUIRE(statusOf(yard) == ProductionStatus::Producing); // active cycle -> green
}
SECTION("Salvage Bay: red when empty, green when holding scrap")
{
Building bay; bay.type = BuildingType::SalvageBay;
bay.outputBuffer.capacity = 20;
REQUIRE(statusOf(bay) == ProductionStatus::Starved); // empty -> red
bay.outputBuffer.items = { makeItem("scrap") };
REQUIRE(statusOf(bay) == ProductionStatus::Producing); // holding scrap -> green
}
}