schematic selection dialog

This commit is contained in:
2026-06-13 12:00:05 +02:00
parent 1641189b75
commit 49f7129bd5
25 changed files with 453 additions and 268 deletions

View File

@@ -7,6 +7,7 @@
#include "GameConfig.h"
#include "HealthComponent.h"
#include "RecipesConfig.h"
#include "SchematicChoiceOption.h"
#include "Simulation.h"
#include "StationBodyComponent.h"
@@ -16,7 +17,7 @@ static GameConfig loadConfig()
}
// Zeros the HP of both enemy defence stations and advances one tick so that
// tickDeathsAndLoot fires, triggering the push and schematic drop.
// tickDeathsAndLoot fires, triggering the push and schematic choices.
static void killEnemyStations(Simulation& sim)
{
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
@@ -30,15 +31,25 @@ static void killEnemyStations(Simulation& sim)
sim.tick();
}
// Kills enemy stations and applies the first schematic choice (index 0).
static void killEnemyStationsAndApply(Simulation& sim)
{
killEnemyStations(sim);
if (sim.hasSchematicChoicesPending())
{
sim.applySchematicChoice(0);
}
}
// Destroys station sets until recipeId is unlocked or maxDestructions is reached.
// Returns true if the recipe is unlocked on exit.
// Applies schematic choice 0 after each destruction. Returns true if unlocked.
static bool awaitRecipeUnlock(Simulation& sim, const std::string& recipeId,
int maxDestructions = 150)
{
for (int i = 0; i < maxDestructions; ++i)
{
if (sim.isRecipeUnlocked(recipeId)) { return true; }
killEnemyStations(sim);
killEnemyStationsAndApply(sim);
}
return sim.isRecipeUnlocked(recipeId);
}
@@ -175,7 +186,7 @@ TEST_CASE("RecipeSchematic: recipe whose output is not implicitly unlocked is ne
Simulation sim(loadConfig());
for (int i = 0; i < 50; ++i)
{
killEnemyStations(sim);
killEnemyStationsAndApply(sim);
}
REQUIRE_FALSE(sim.isRecipeUnlocked("exotic_alloy"));
}
@@ -186,7 +197,7 @@ TEST_CASE("RecipeSchematic: recipe with level > destroyed station level is not a
// advanced_circuit has unlock_at_station_level = 1. Destroying a single
// level-0 station set must not award it regardless of the RNG outcome.
Simulation sim(loadConfig());
killEnemyStations(sim);
killEnemyStationsAndApply(sim);
REQUIRE_FALSE(sim.isRecipeUnlocked("advanced_circuit"));
}
@@ -209,25 +220,35 @@ TEST_CASE("RecipeSchematic: awarded recipe schematic stays unlocked and is not a
// Destroy 30 more station sets; the recipe is no longer in the pool.
for (int i = 0; i < 30; ++i)
{
killEnemyStations(sim);
killEnemyStationsAndApply(sim);
}
REQUIRE(sim.isRecipeUnlocked("quick_circuit"));
}
TEST_CASE("RecipeSchematic: no SchematicDropEvent is emitted for a recipe schematic drop",
TEST_CASE("RecipeSchematic: recipe schematic can appear in pending choices",
"[recipe_schematic]")
{
Simulation sim(loadConfig());
sim.drainSchematicDropEvents(); // clear any startup events
awaitRecipeUnlock(sim, "quick_circuit");
const std::vector<SchematicDropEvent> events = sim.drainSchematicDropEvents();
for (const SchematicDropEvent& ev : events)
bool foundRecipeChoice = false;
for (int i = 0; i < 150 && !foundRecipeChoice; ++i)
{
CHECK(ev.schematicId != "quick_circuit");
killEnemyStations(sim);
if (sim.hasSchematicChoicesPending())
{
for (const SchematicChoiceOption& opt : sim.getPendingSchematicChoices())
{
if (opt.type == SchematicType::Recipe)
{
foundRecipeChoice = true;
break;
}
}
sim.applySchematicChoice(0);
}
}
CHECK(foundRecipeChoice);
}
// ---------------------------------------------------------------------------