schematic selection dialog
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -61,11 +61,11 @@ TEST_CASE("Simulation::drainFireEvents clears queue on drain", "[simulation]")
|
||||
REQUIRE(sim.drainFireEvents().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Simulation::drainSchematicDropEvents returns empty initially", "[simulation]")
|
||||
TEST_CASE("Simulation::hasSchematicChoicesPending returns false initially", "[simulation]")
|
||||
{
|
||||
Simulation sim(loadConfig());
|
||||
|
||||
REQUIRE(sim.drainSchematicDropEvents().empty());
|
||||
REQUIRE_FALSE(sim.hasSchematicChoicesPending());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "StationBodyComponent.h"
|
||||
#include "WeaponComponent.h"
|
||||
#include "ModulesConfig.h"
|
||||
#include "RecipesConfig.h"
|
||||
#include "SchematicChoiceOption.h"
|
||||
#include "ShipsConfig.h"
|
||||
#include "Simulation.h"
|
||||
#include "Tick.h"
|
||||
@@ -247,7 +249,7 @@ TEST_CASE("WaveSystem: destroying both enemy stations triggers a push", "[wave]"
|
||||
REQUIRE(enemyCount == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: push emits exactly one SchematicDropEvent", "[wave]")
|
||||
TEST_CASE("WaveSystem: push generates pending schematic choices", "[wave]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
@@ -259,11 +261,13 @@ TEST_CASE("WaveSystem: push emits exactly one SchematicDropEvent", "[wave]")
|
||||
|
||||
sim.tick();
|
||||
|
||||
const std::vector<SchematicDropEvent> events = sim.drainSchematicDropEvents();
|
||||
REQUIRE(events.size() == 1);
|
||||
REQUIRE(sim.hasSchematicChoicesPending());
|
||||
const std::vector<SchematicChoiceOption>& choices = sim.getPendingSchematicChoices();
|
||||
REQUIRE(choices.size() >= 1);
|
||||
REQUIRE(choices.size() <= 3);
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: push schematic drop awards a known ship id", "[wave]")
|
||||
TEST_CASE("WaveSystem: push schematic choices have valid ids", "[wave]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
@@ -274,23 +278,68 @@ TEST_CASE("WaveSystem: push schematic drop awards a known ship id", "[wave]")
|
||||
});
|
||||
|
||||
sim.tick();
|
||||
const std::vector<SchematicDropEvent> events = sim.drainSchematicDropEvents();
|
||||
REQUIRE(events.size() == 1);
|
||||
const std::vector<SchematicChoiceOption>& choices = sim.getPendingSchematicChoices();
|
||||
REQUIRE_FALSE(choices.empty());
|
||||
|
||||
bool validId = false;
|
||||
for (const ShipDef& def : sim.config().ships.ships)
|
||||
for (const SchematicChoiceOption& opt : choices)
|
||||
{
|
||||
if (def.id == events[0].schematicId) { validId = true; break; }
|
||||
}
|
||||
if (!validId)
|
||||
{
|
||||
for (const ModuleDef& def : sim.config().modules.modules)
|
||||
bool validId = false;
|
||||
for (const ShipDef& def : sim.config().ships.ships)
|
||||
{
|
||||
if (def.id == events[0].schematicId) { validId = true; break; }
|
||||
if (def.id == opt.schematicId) { validId = true; break; }
|
||||
}
|
||||
if (!validId)
|
||||
{
|
||||
for (const ModuleDef& def : sim.config().modules.modules)
|
||||
{
|
||||
if (def.id == opt.schematicId) { validId = true; break; }
|
||||
}
|
||||
}
|
||||
if (!validId)
|
||||
{
|
||||
for (const RecipeDef& def : sim.config().recipes.recipes)
|
||||
{
|
||||
if (def.id == opt.schematicId) { validId = true; break; }
|
||||
}
|
||||
}
|
||||
REQUIRE(validId);
|
||||
}
|
||||
REQUIRE(validId);
|
||||
REQUIRE(events[0].newLevel >= 1);
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: schematic choices have no duplicates", "[wave]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h)
|
||||
{
|
||||
if (f.isEnemy) { h.hp = -1.0f; }
|
||||
});
|
||||
|
||||
sim.tick();
|
||||
const std::vector<SchematicChoiceOption>& choices = sim.getPendingSchematicChoices();
|
||||
std::set<std::string> ids;
|
||||
for (const SchematicChoiceOption& opt : choices)
|
||||
{
|
||||
ids.insert(opt.schematicId);
|
||||
}
|
||||
REQUIRE(ids.size() == choices.size());
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: applySchematicChoice clears pending and applies", "[wave]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
sim.admin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
||||
[](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h)
|
||||
{
|
||||
if (f.isEnemy) { h.hp = -1.0f; }
|
||||
});
|
||||
|
||||
sim.tick();
|
||||
REQUIRE(sim.hasSchematicChoicesPending());
|
||||
sim.applySchematicChoice(0);
|
||||
REQUIRE_FALSE(sim.hasSchematicChoicesPending());
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: push places new enemy stations further right", "[wave]")
|
||||
|
||||
Reference in New Issue
Block a user