feat: extend EndGen

This commit is contained in:
dualfroz committed 2026-09-07 19:00:16 +02:00
1 parent 6c90768047
commit 8884fe5a53
1 file changed
+184
+184
View File
@@ -158,3 +158,187 @@ std::vector<ObsidianPillar> EndGenerator::pillars() const {
std::vector<ObsidianPillar> ring;
ring.reserve(static_cast<std::size_t>(params.pillarCount));
for (int index = 0; index < params.pillarCount; ++index) {
float angle = 6.2831853f * static_cast<float>(index) /
static_cast<float>(params.pillarCount);
uint32_t roll = mixCoord(params.seed ^ 0x50494c4cu, index, 0, params.pillarRing);
int spread = params.pillarRing + static_cast<int>(roll % 9u) - 4;
int gx = static_cast<int>(std::lround(std::cos(angle) * static_cast<float>(spread)));
int gz = static_cast<int>(std::lround(std::sin(angle) * static_cast<float>(spread)));
ObsidianPillar pillar;
pillar.base = {gx, surfaceHeight(gx, gz), gz};
pillar.radius = 1 + static_cast<int>((roll >> 8) % 3u);
pillar.height = 22 + static_cast<int>((roll >> 12) % 19u);
pillar.crowned = pillar.height >= 34;
int top = pillar.base.y + pillar.height;
if (top >= params.ceilingY - 2) pillar.height = params.ceilingY - 3 - pillar.base.y;
ring.push_back(pillar);
}
return ring;
}
void EndGenerator::generateChunk(Chunk& chunk, const Vec3i& chunkCoord) const {
int ox = chunkCoord.x * chunkSize;
int oy = chunkCoord.y * chunkSize;
int oz = chunkCoord.z * chunkSize;
for (int lx = 0; lx < chunkSize; ++lx) {
for (int lz = 0; lz < chunkSize; ++lz) {
int topY = 0, bottomY = 0;
bool solidColumn = columnExtent(ox + lx, oz + lz, topY, bottomY);
for (int ly = 0; ly < chunkSize; ++ly) {
int gy = oy + ly;
bool stone = solidColumn && gy >= bottomY && gy <= topY;
chunk.set(lx, ly, lz, stone ? Block::EndStone : Block::Air);
}
}
}
}
void EndGenerator::stampPillars(World& world, int minX, int minZ, int maxX, int maxZ) const {
for (const ObsidianPillar& pillar : pillars()) {
if (pillar.height <= 0) continue;
int reach = pillar.radius;
for (int dx = -reach; dx <= reach; ++dx) {
for (int dz = -reach; dz <= reach; ++dz) {
if (dx * dx + dz * dz > reach * reach) continue;
int gx = pillar.base.x + dx;
int gz = pillar.base.z + dz;
if (gx < minX || gx > maxX || gz < minZ || gz > maxZ) continue;
int bottom = pillar.base.y - 3;
if (bottom < params.floorY) bottom = params.floorY;
for (int y = bottom; y <= pillar.base.y + pillar.height; ++y)
world.setBlock(gx, y, gz, Block::Obsidian);
}
}
if (!pillar.crowned) continue;
int crownX = pillar.base.x;
int crownZ = pillar.base.z;
if (crownX < minX || crownX > maxX || crownZ < minZ || crownZ > maxZ) continue;
world.setBlock(crownX, pillar.base.y + pillar.height + 1, crownZ, Block::CryingObsidian);
}
}
void EndGenerator::placePillars(World& world) const {
stampPillars(world, -unboundedSpan, -unboundedSpan, unboundedSpan, unboundedSpan);
}
void EndGenerator::stampExitPortal(World& world, int minX, int minZ, int maxX, int maxZ) const {
int surface = surfaceHeight(0, 0);
if (surface < params.floorY) return;
for (int dx = -2; dx <= 2; ++dx) {
for (int dz = -2; dz <= 2; ++dz) {
if (dx < minX || dx > maxX || dz < minZ || dz > maxZ) continue;
world.setBlock(dx, surface - 1, dz, Block::Bedrock);
bool inner = std::abs(dx) <= 1 && std::abs(dz) <= 1;
world.setBlock(dx, surface, dz, inner ? Block::EndPortal : Block::Bedrock);
for (int up = 1; up <= 3; ++up) world.setBlock(dx, surface + up, dz, Block::Air);
}
}
const int cornerX[4] = {-2, -2, 2, 2};
const int cornerZ[4] = {-2, 2, -2, 2};
for (int corner = 0; corner < 4; ++corner) {
int gx = cornerX[corner];
int gz = cornerZ[corner];
if (gx < minX || gx > maxX || gz < minZ || gz > maxZ) continue;
for (int up = 1; up <= 3; ++up) world.setBlock(gx, surface + up, gz, Block::Bedrock);
}
}
void EndGenerator::placeExitPortal(World& world) const {
stampExitPortal(world, -unboundedSpan, -unboundedSpan, unboundedSpan, unboundedSpan);
}
int EndGenerator::growBranch(World& world, Vec3i pos, int depth, int maxDepth,
uint32_t& rngState) const {
if (pos.y >= params.ceilingY - 2 || depth >= maxDepth) {
if (world.blockAt(pos.x, pos.y, pos.z) != Block::Air) return 0;
world.setBlock(pos.x, pos.y, pos.z, Block::ChorusFlower);
return 1;
}
int written = 0;
int segments = 1 + static_cast<int>(nextState(rngState) % 3u);
for (int step = 0; step < segments && pos.y < params.ceilingY - 2; ++step) {
if (world.blockAt(pos.x, pos.y, pos.z) != Block::Air) break;
world.setBlock(pos.x, pos.y, pos.z, Block::ChorusPlant);
++written;
pos.y += 1;
}
int branches = static_cast<int>(nextState(rngState) % 3u);
for (int branch = 0; branch < branches; ++branch) {
const Vec3i& step = horizontalSteps[nextState(rngState) % 4u];
Vec3i side{pos.x + step.x, pos.y - 1, pos.z + step.z};
if (world.blockAt(side.x, side.y, side.z) != Block::Air) continue;
world.setBlock(side.x, side.y, side.z, Block::ChorusPlant);
++written;
written += growBranch(world, {side.x, side.y + 1, side.z}, depth + 1, maxDepth, rngState);
}
written += growBranch(world, pos, depth + 1, maxDepth, rngState);
return written;
}
int EndGenerator::growChorus(World& world, const Vec3i& base, uint32_t roll, int maxDepth) const {
if (world.blockAt(base.x, base.y - 1, base.z) != Block::EndStone) return 0;
if (world.blockAt(base.x, base.y, base.z) != Block::Air) return 0;
uint32_t state = mixCoord(params.seed ^ 0x43484f52u, base.x, base.y, base.z) ^ roll;
return growBranch(world, base, 0, maxDepth, state);
}
void EndGenerator::raisePurpurSpire(World& world, int gx, int gz, int top, uint32_t roll) const {
int tall = 2 + static_cast<int>((roll >> 18) % 4u);
for (int step = 0; step < tall; ++step) {
int y = top + 1 + step;
if (y >= params.ceilingY - 1) return;
if (world.blockAt(gx, y, gz) != Block::Air) return;
world.setBlock(gx, y, gz, Block::Purpur);
}
}
void EndGenerator::decorateColumn(World& world, int columnX, int columnZ) const {
int ox = columnX * chunkSize;
int oz = columnZ * chunkSize;
for (int lx = 4; lx < chunkSize - 4; ++lx) {
for (int lz = 4; lz < chunkSize - 4; ++lz) {
int gx = ox + lx;
int gz = oz + lz;
if (biomeAt(gx, gz) != EndBiome::OuterIsland) continue;
int top = surfaceHeight(gx, gz);
if (top < params.floorY) continue;
uint32_t roll = mixCoord(params.seed ^ 0x44454330u, gx, 0, gz);
if (params.chorus && roll % 19u == 0u) growChorus(world, {gx, top + 1, gz}, roll, 3);
else if (roll % 53u == 7u) raisePurpurSpire(world, gx, gz, top, roll);
}
}
}
void EndGenerator::generateColumn(World& world, int columnX, int columnZ) const {
for (int cy = 0; cy < stackChunkCount(); ++cy) {
Chunk& chunk = world.ensureChunk({columnX, cy, columnZ});
generateChunk(chunk, {columnX, cy, columnZ});
}
int minX = columnX * chunkSize;
int minZ = columnZ * chunkSize;
int maxX = minX + chunkSize - 1;
int maxZ = minZ + chunkSize - 1;
stampPillars(world, minX, minZ, maxX, maxZ);
if (params.exitPortal) stampExitPortal(world, minX, minZ, maxX, maxZ);
decorateColumn(world, columnX, columnZ);
}
} // namespace vf