19 test translation units each defined an identical local loadConfig(). They now include src/test/TestConfig.h, which lives off the lib/ui/app include path like SimulationTestAccess.h. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
114 lines
4.2 KiB
C++
114 lines
4.2 KiB
C++
// Unlock groups as the unit of loot (REQ-LOCK-EXPLICIT, REQ-LOCK-BUILDING,
|
|
// REQ-DEF-SCHEMATIC-DROP): buildings can be locked, and one drop can grant a
|
|
// building and a module together.
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "catch.hpp"
|
|
|
|
#include "BuildingType.h"
|
|
#include "ConfigLoader.h"
|
|
#include "FactionComponent.h"
|
|
#include "GameConfig.h"
|
|
#include "HealthComponent.h"
|
|
#include "SchematicChoiceOption.h"
|
|
#include "Simulation.h"
|
|
#include "SimulationTestAccess.h"
|
|
#include "StationBodyComponent.h"
|
|
#include "UnlocksConfig.h"
|
|
#include "TestConfig.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
void killEnemyStations(Simulation& sim)
|
|
{
|
|
sim.getAdmin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
|
|
[](entt::entity, StationBodyComponent&, FactionComponent& faction, HealthComponent& health)
|
|
{
|
|
if (faction.isEnemy) { health.hp = 0.0f; }
|
|
});
|
|
sim.tick();
|
|
}
|
|
|
|
// A config whose only unlock group grants the salvager module and the salvage
|
|
// bay together, gated to the given station level.
|
|
GameConfig configWithSalvageGroup(int stationLevel)
|
|
{
|
|
GameConfig cfg = loadTestConfig();
|
|
cfg.unlocks.groups.clear();
|
|
cfg.unlocks.groups.push_back(
|
|
UnlockGroupDef{"salvage_operations", stationLevel, {}, {}, {"salvager"}, {"salvage_bay"}, {}});
|
|
return cfg;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("UnlockGroup: a building granted by a group is locked at game start", "[unlock_group]")
|
|
{
|
|
const Simulation sim(configWithSalvageGroup(0));
|
|
CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
|
|
CHECK_FALSE(sim.isModuleSchematicUnlocked("salvager"));
|
|
// A building in no unlock group is available from the start.
|
|
CHECK(sim.isBuildingUnlocked(BuildingType::Miner));
|
|
}
|
|
|
|
TEST_CASE("UnlockGroup: the drop option lists every granted item", "[unlock_group]")
|
|
{
|
|
Simulation sim(configWithSalvageGroup(0));
|
|
killEnemyStations(sim); // level 0
|
|
REQUIRE(sim.hasSchematicChoicesPending());
|
|
|
|
const std::vector<SchematicChoiceOption>& options = sim.getPendingSchematicChoices();
|
|
const auto it = std::find_if(options.begin(), options.end(),
|
|
[](const SchematicChoiceOption& o) { return o.unlockGroupId == "salvage_operations"; });
|
|
REQUIRE(it != options.end());
|
|
|
|
const bool grantsModule = std::any_of(it->grantedItems.begin(), it->grantedItems.end(),
|
|
[](const GrantedSchematic& g) { return g.type == SchematicType::Module && g.id == "salvager"; });
|
|
const bool grantsBuilding = std::any_of(it->grantedItems.begin(), it->grantedItems.end(),
|
|
[](const GrantedSchematic& g) { return g.type == SchematicType::Building && g.id == "salvage_bay"; });
|
|
CHECK(grantsModule);
|
|
CHECK(grantsBuilding);
|
|
}
|
|
|
|
TEST_CASE("UnlockGroup: awarding a group unlocks the building and module at once", "[unlock_group]")
|
|
{
|
|
Simulation sim(configWithSalvageGroup(0));
|
|
killEnemyStations(sim); // level 0
|
|
REQUIRE(sim.hasSchematicChoicesPending());
|
|
|
|
// The only eligible group is salvage_operations, so option 0 grants it.
|
|
REQUIRE(sim.getPendingSchematicChoices()[0].unlockGroupId == "salvage_operations");
|
|
SimulationTestAccess::applySchematicChoice(sim, 0);
|
|
|
|
CHECK(sim.isBuildingUnlocked(BuildingType::SalvageBay));
|
|
CHECK(sim.isModuleSchematicUnlocked("salvager"));
|
|
}
|
|
|
|
TEST_CASE("UnlockGroup: a group above the destroyed station level is not offered", "[unlock_group]")
|
|
{
|
|
Simulation sim(configWithSalvageGroup(5)); // eligible only at level >= 5
|
|
killEnemyStations(sim); // level 0 push
|
|
// Pool is empty at level 0, so no choice dialog opens at all.
|
|
CHECK_FALSE(sim.hasSchematicChoicesPending());
|
|
CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
|
|
}
|
|
|
|
TEST_CASE("UnlockGroup: reset re-locks a previously awarded building", "[unlock_group]")
|
|
{
|
|
Simulation sim(configWithSalvageGroup(0));
|
|
killEnemyStations(sim);
|
|
REQUIRE(sim.getPendingSchematicChoices()[0].unlockGroupId == "salvage_operations");
|
|
SimulationTestAccess::applySchematicChoice(sim, 0);
|
|
REQUIRE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
|
|
|
|
sim.reset();
|
|
|
|
CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
|
|
CHECK_FALSE(sim.isModuleSchematicUnlocked("salvager"));
|
|
}
|