diff --git a/tests/testEndGen.cpp b/tests/testEndGen.cpp index 3f7e2f5..22a39b3 100644 --- a/tests/testEndGen.cpp +++ b/tests/testEndGen.cpp @@ -154,3 +154,201 @@ VF_TEST(obsidianPillarsRingTheCentreDeterministically) { CHECK_EQ(ring.size(), static_cast(params.pillarCount)); CHECK_EQ(again.size(), ring.size()); + std::set heights; + int crowned = 0; + for (std::size_t index = 0; index < ring.size(); ++index) { + const ObsidianPillar& pillar = ring[index]; + CHECK_EQ(pillar.base.x, again[index].base.x); + CHECK_EQ(pillar.base.z, again[index].base.z); + CHECK_EQ(pillar.height, again[index].height); + + float distance = distanceFromOrigin(pillar.base.x, pillar.base.z); + CHECK(distance > static_cast(params.pillarRing) - 6.0f); + CHECK(distance < static_cast(params.pillarRing) + 6.0f); + CHECK(pillar.radius >= 1 && pillar.radius <= 3); + CHECK(pillar.base.y + pillar.height < params.ceilingY - 1); + heights.insert(pillar.height); + if (pillar.crowned) ++crowned; + } + CHECK(heights.size() > 1); + CHECK(crowned > 0); +} + +VF_TEST(pillarsAreStampedIntoTheGeneratedWorld) { + EndGenerator generator; + World world; + for (int columnX = -4; columnX <= 4; ++columnX) + for (int columnZ = -4; columnZ <= 4; ++columnZ) + generator.generateColumn(world, columnX, columnZ); + + ObsidianPillar pillar = generator.pillars().front(); + CHECK_EQ(world.blockAt(pillar.base.x, pillar.base.y, pillar.base.z), Block::Obsidian); + CHECK_EQ(world.blockAt(pillar.base.x, pillar.base.y + pillar.height, pillar.base.z), + Block::Obsidian); + CHECK_EQ(world.blockAt(pillar.base.x, pillar.base.y + pillar.height + 2, pillar.base.z), + Block::Air); +} + +VF_TEST(exitPortalSitsAtTheOriginOfTheCentralIsland) { + EndGenerator generator; + World world; + for (int columnX = -1; columnX <= 0; ++columnX) + for (int columnZ = -1; columnZ <= 0; ++columnZ) + generator.generateColumn(world, columnX, columnZ); + + int surface = generator.surfaceHeight(0, 0); + CHECK_EQ(world.blockAt(0, surface, 0), Block::EndPortal); + CHECK_EQ(world.blockAt(1, surface, 1), Block::EndPortal); + CHECK_EQ(world.blockAt(2, surface, 0), Block::Bedrock); + CHECK_EQ(world.blockAt(0, surface - 1, 0), Block::Bedrock); + CHECK_EQ(world.blockAt(-2, surface + 1, -2), Block::Bedrock); + CHECK_EQ(world.blockAt(0, surface + 1, 0), Block::Air); + + int portalCells = 0; + for (int dx = -2; dx <= 2; ++dx) + for (int dz = -2; dz <= 2; ++dz) + if (world.blockAt(dx, surface, dz) == Block::EndPortal) ++portalCells; + CHECK_EQ(portalCells, 9); +} + +VF_TEST(exitPortalCanBeSwitchedOff) { + EndGenerator::Params bare; + bare.exitPortal = false; + EndGenerator generator(bare); + World world; + generator.generateColumn(world, 0, 0); + + CHECK_EQ(world.blockAt(0, generator.surfaceHeight(0, 0), 0), Block::EndStone); +} + +VF_TEST(chorusGrowsAStemWithBranchesAndFlowers) { + EndGenerator generator; + World world; + world.setBlock(0, 50, 0, Block::EndStone); + + int written = generator.growChorus(world, {0, 51, 0}, 4711u, 3); + CHECK(written > 3); + + int stems = 0; + int flowers = 0; + int highest = 50; + int widest = 0; + for (int gx = -8; gx <= 8; ++gx) { + for (int gz = -8; gz <= 8; ++gz) { + for (int gy = 50; gy < 90; ++gy) { + Block block = world.blockAt(gx, gy, gz); + if (block != Block::ChorusPlant && block != Block::ChorusFlower) continue; + if (block == Block::ChorusPlant) ++stems; + else ++flowers; + if (gy > highest) highest = gy; + int reach = std::abs(gx) > std::abs(gz) ? std::abs(gx) : std::abs(gz); + if (reach > widest) widest = reach; + } + } + } + CHECK_EQ(stems + flowers, written); + CHECK(stems > 0); + CHECK(flowers > 0); + CHECK(highest > 51); + CHECK(widest > 0); + CHECK(widest <= 3); +} + +VF_TEST(chorusGrowthIsRepeatableAndBoundedByItsDepth) { + EndGenerator generator; + + World first, second; + first.setBlock(0, 50, 0, Block::EndStone); + second.setBlock(0, 50, 0, Block::EndStone); + int written = generator.growChorus(first, {0, 51, 0}, 88u, 3); + CHECK_EQ(generator.growChorus(second, {0, 51, 0}, 88u, 3), written); + + int differences = 0; + for (int gx = -8; gx <= 8; ++gx) + for (int gz = -8; gz <= 8; ++gz) + for (int gy = 50; gy < 90; ++gy) + if (first.blockAt(gx, gy, gz) != second.blockAt(gx, gy, gz)) ++differences; + CHECK_EQ(differences, 0); + + World shallow; + shallow.setBlock(0, 50, 0, Block::EndStone); + int small = generator.growChorus(shallow, {0, 51, 0}, 88u, 1); + CHECK(small > 0); + CHECK(small < written); + + int shallowTop = 50; + for (int gy = 50; gy < 90; ++gy) + if (shallow.blockAt(0, gy, 0) != Block::Air) shallowTop = gy; + CHECK(shallowTop <= 54); +} + +VF_TEST(chorusRefusesAnythingButEndStoneUnderneath) { + EndGenerator generator; + World world; + + CHECK_EQ(generator.growChorus(world, {0, 51, 0}, 1u, 3), 0); + + world.setBlock(0, 50, 0, Block::Obsidian); + CHECK_EQ(generator.growChorus(world, {0, 51, 0}, 1u, 3), 0); + + world.setBlock(0, 50, 0, Block::EndStone); + world.setBlock(0, 51, 0, Block::Purpur); + CHECK_EQ(generator.growChorus(world, {0, 51, 0}, 1u, 3), 0); +} + +VF_TEST(outerIslandsOnlyAppearBeyondTheDistanceThreshold) { + EndGenerator generator; + int islandX = 0; + int islandZ = 0; + CHECK(locateOuterIsland(generator, islandX, islandZ)); + CHECK(distanceFromOrigin(islandX, islandZ) >= + static_cast(generator.settings().outerDistance)); + CHECK_EQ(generator.biomeAt(islandX, islandZ), EndBiome::OuterIsland); + CHECK(!generator.islandAt(islandX, islandZ).central); + CHECK(generator.islandAt(islandX, islandZ).radius > 0.0f); +} + +VF_TEST(outerIslandsCarryChorusAndPurpur) { + EndGenerator generator; + int islandX = 0; + int islandZ = 0; + CHECK(locateOuterIsland(generator, islandX, islandZ)); + + World world; + int columnX = floorDiv(islandX, chunkSize); + int columnZ = floorDiv(islandZ, chunkSize); + for (int dx = -1; dx <= 1; ++dx) + for (int dz = -1; dz <= 1; ++dz) + generator.generateColumn(world, columnX + dx, columnZ + dz); + + int minX = (columnX - 1) * chunkSize; + int minZ = (columnZ - 1) * chunkSize; + int endStone = tallyBlocks(world, Block::EndStone, minX, minZ, chunkSize * 3, 128); + int chorusStems = tallyBlocks(world, Block::ChorusPlant, minX, minZ, chunkSize * 3, 128); + int chorusFlowers = tallyBlocks(world, Block::ChorusFlower, minX, minZ, chunkSize * 3, 128); + + CHECK(endStone > 1000); + CHECK(chorusStems > 0); + CHECK(chorusFlowers > 0); + + // Nothing from the central island's furniture leaks out here. + CHECK_EQ(tallyBlocks(world, Block::EndPortal, minX, minZ, chunkSize * 3, 128), 0); + CHECK_EQ(tallyBlocks(world, Block::Obsidian, minX, minZ, chunkSize * 3, 128), 0); +} + +VF_TEST(endGenerationStaysInsideTheColumnItWasAskedFor) { + EndGenerator generator; + int islandX = 0; + int islandZ = 0; + CHECK(locateOuterIsland(generator, islandX, islandZ)); + + World world; + int columnX = floorDiv(islandX, chunkSize); + int columnZ = floorDiv(islandZ, chunkSize); + generator.generateColumn(world, columnX, columnZ); + + int strays = 0; + for (const auto& entry : world.allChunks()) + if (entry.first.x != columnX || entry.first.z != columnZ) ++strays; + CHECK_EQ(strays, 0); +}