feat: extend EndGen
This commit is contained in:
1 parent
6c90768047
commit
8884fe5a53
1 file changed
+184
@@ -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
|
||||
Reference in new issue
Block a user