test: extend coverage of testSpawning

This commit is contained in:
dualfroz committed 2026-09-13 18:22:25 +02:00
1 parent 832d5f6b8b
commit 5dbfacf76f
1 file changed
+158
+158
View File
@@ -314,3 +314,161 @@ VF_TEST(aRunningCycleStopsSpawningOnceTheCapIsReached) {
spawner.runCycle(env, SpawnCategory::Hostile, columns, 1234u + static_cast<uint64_t>(cycle));
}
CHECK(env.populationOf(SpawnCategory::Hostile) > 0);
CHECK(env.populationOf(SpawnCategory::Hostile) <= limit + 4);
CHECK(spawner.denialCount(SpawnDenial::CategoryCapReached) > 0);
}
VF_TEST(aPackArrivesSpreadAroundItsOriginCell) {
World world = plateWorld(Block::Grass);
LightField light = bakeLight(world);
SpawnEnvironment env = nightEnvironment(world, light);
SpawnRule rule = zombieRule();
rule.packMin = 4;
rule.packMax = 4;
rule.packSpread = 3;
Spawner spawner({rule});
SpawnTrial trial;
trial.cell = originCell();
trial.species = Species::Zombie;
trial.category = SpawnCategory::Hostile;
trial.ruleFound = true;
trial.cellFound = true;
trial.packSize = 4;
SpawnResult result = spawner.attemptSpawn(env, rule, trial, 4242u);
CHECK(result.spawned);
CHECK(result.denial == SpawnDenial::None);
CHECK(result.packSize() >= 2);
CHECK(result.packSize() <= 4);
CHECK(result.role == BehaviorRole::HostileHunter);
for (const Vec3& spot : result.positions) {
CHECK(std::fabs(spot.x - 0.5f) <= static_cast<float>(rule.packSpread));
CHECK(std::fabs(spot.z - 0.5f) <= static_cast<float>(rule.packSpread));
CHECK(world.solidAt(static_cast<int>(std::floor(spot.x)), floorY,
static_cast<int>(std::floor(spot.z))));
}
}
VF_TEST(aSpawnedPackBecomesAgentsCarryingTheRightBehaviourRole) {
World world = plateWorld(Block::Grass);
LightField light = bakeLight(world);
SpawnEnvironment env = nightEnvironment(world, light);
SpawnRule rule = zombieRule();
rule.packMin = 3;
rule.packMax = 3;
Spawner spawner({rule});
SpawnTrial trial;
trial.cell = originCell();
trial.species = Species::Zombie;
trial.ruleFound = true;
trial.cellFound = true;
trial.packSize = 3;
SpawnResult result = spawner.attemptSpawn(env, rule, trial, 77u);
CHECK(result.spawned);
std::vector<BehaviorAgent> agents = agentsFromSpawn(result, 5u);
CHECK_EQ(agents.size(), result.positions.size());
const SpeciesStats& stats = speciesStats(Species::Zombie);
for (const BehaviorAgent& agent : agents) {
CHECK(agent.species == Species::Zombie);
CHECK_NEAR(agent.health, stats.maxHealth, 1e-6f);
CHECK_NEAR(agent.maxHealth, stats.maxHealth, 1e-6f);
CHECK(agent.moveStep > 0.0f);
CHECK(agent.rng != 0u);
}
CHECK(makeTreeForRole(result.role).structureValid());
SpawnResult refused;
CHECK(agentsFromSpawn(refused, 5u).empty());
}
VF_TEST(despawnNeverTouchesAMobStandingNextToThePlayer) {
DespawnPolicy policy;
DespawnCandidate mob;
mob.position = {10.0f, 64.0f, 0.0f};
Vec3 player{0.0f, 64.0f, 0.0f};
CHECK(despawnBandFor(policy, mob.position, player) == DespawnBand::Protected);
uint32_t rng = 12345u;
for (int tick = 0; tick < 2000; ++tick) {
CHECK(!shouldDespawn(policy, mob, player, rng));
}
}
VF_TEST(despawnRemovesAMobBeyondTheHardDistanceImmediately) {
DespawnPolicy policy;
DespawnCandidate mob;
mob.position = {200.0f, 64.0f, 0.0f};
Vec3 player{0.0f, 64.0f, 0.0f};
CHECK(despawnBandFor(policy, mob.position, player) == DespawnBand::Immediate);
uint32_t rng = 1u;
CHECK(shouldDespawn(policy, mob, player, rng));
CHECK_EQ(rng, 1u);
}
VF_TEST(despawnInTheMiddleBandIsARollThatMatchesItsStatedOdds) {
DespawnPolicy policy;
policy.randomChanceDenominator = 50;
DespawnCandidate mob;
mob.position = {64.0f, 64.0f, 0.0f};
Vec3 player{0.0f, 64.0f, 0.0f};
CHECK(despawnBandFor(policy, mob.position, player) == DespawnBand::Random);
int hits = 0;
uint32_t rng = 0x1337u;
for (int tick = 0; tick < 5000; ++tick) {
if (shouldDespawn(policy, mob, player, rng)) ++hits;
}
CHECK(hits > 40);
CHECK(hits < 180);
// Same seed, same sequence of rolls.
int replay = 0;
uint32_t again = 0x1337u;
for (int tick = 0; tick < 5000; ++tick) {
if (shouldDespawn(policy, mob, player, again)) ++replay;
}
CHECK_EQ(replay, hits);
}
VF_TEST(namedAndLeashedMobsSurviveEvenAtTheEdgeOfTheWorld) {
DespawnPolicy policy;
Vec3 player{0.0f, 64.0f, 0.0f};
uint32_t rng = 9u;
DespawnCandidate named;
named.position = {500.0f, 64.0f, 0.0f};
named.named = true;
CHECK(despawnCandidateIsProtected(policy, named));
CHECK(!shouldDespawn(policy, named, player, rng));
DespawnCandidate leashed;
leashed.position = {500.0f, 64.0f, 0.0f};
leashed.leashed = true;
CHECK(!shouldDespawn(policy, leashed, player, rng));
DespawnCandidate persistent;
persistent.position = {500.0f, 64.0f, 0.0f};
persistent.persistent = true;
CHECK(!shouldDespawn(policy, persistent, player, rng));
DespawnPolicy ruthless;
ruthless.despawnNamed = true;
CHECK(!despawnCandidateIsProtected(ruthless, named));
CHECK(shouldDespawn(ruthless, named, player, rng));
}
VF_TEST(theSpawnerBlockCountsDownActivatesAndRespectsItsNearbyLimit) {
World world = plateWorld(Block::Grass);
LightField light = bakeLight(world);
SpawnEnvironment env = nightEnvironment(world, light);
env.playerPosition = {0.5f, static_cast<float>(spawnY), 0.5f};