mirror of
https://github.com/Minecraft-Community-Edition/client.git
synced 2026-05-23 01:24:32 +00:00
improve QuicksoilShelfFeature generation and placement
This commit is contained in:
@@ -7,56 +7,146 @@ QuicksoilShelfFeature::QuicksoilShelfFeature() : Feature(false)
|
||||
{
|
||||
}
|
||||
|
||||
static bool isIslandBlock(int tile)
|
||||
{
|
||||
return tile == Tile::holystone_Id || tile == Tile::aetherDirt_Id;
|
||||
}
|
||||
|
||||
bool QuicksoilShelfFeature::place(Level *level, Random *random, int x, int y, int z)
|
||||
{
|
||||
// Find the bottom of the island at this XZ column
|
||||
// Start from the given Y and scan downward to find the lowest solid block
|
||||
int bottomY = -1;
|
||||
// scan down to find holystone/aether dirt
|
||||
int placeY = -1;
|
||||
for (int yy = y; yy >= 1; yy--)
|
||||
{
|
||||
int tile = level->getTile(x, yy, z);
|
||||
if (tile != 0)
|
||||
if (isIslandBlock(tile))
|
||||
{
|
||||
// Check if the block below is air — this is the island bottom
|
||||
if (level->getTile(x, yy - 1, z) == 0)
|
||||
placeY = yy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (placeY < 1) return false;
|
||||
|
||||
// make sure this block is actually exposed and not buried inside the island
|
||||
bool exposedToAir = false;
|
||||
int neighborOffsets[][3] = { {1,0,0}, {-1,0,0}, {0,0,1}, {0,0,-1}, {0,-1,0}, {0,1,0} };
|
||||
for (int n = 0; n < 6; n++)
|
||||
{
|
||||
if (level->getTile(x + neighborOffsets[n][0], placeY + neighborOffsets[n][1], z + neighborOffsets[n][2]) == 0)
|
||||
{
|
||||
exposedToAir = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exposedToAir) return false;
|
||||
|
||||
// need more island blocks above so we know this isn't just a tree or random block
|
||||
bool hasIslandNeighborAbove = false;
|
||||
for (int cy = placeY + 1; cy <= placeY + 3; cy++)
|
||||
{
|
||||
if (isIslandBlock(level->getTile(x, cy, z)) || level->getTile(x, cy, z) == Tile::aetherGrass_Id)
|
||||
{
|
||||
hasIslandNeighborAbove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasIslandNeighborAbove) return false;
|
||||
|
||||
// check that we're actually near the edge of the island by looking
|
||||
// for open air in at least one cardinal direction
|
||||
const int scanDist = 8;
|
||||
const int airThreshold = 5;
|
||||
bool nearEdge = false;
|
||||
|
||||
int dirX[] = { 0, 0, 1, -1 };
|
||||
int dirZ[] = { 1, -1, 0, 0 };
|
||||
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int airCount = 0;
|
||||
for (int i = 1; i <= scanDist; i++)
|
||||
{
|
||||
int sx = x + dirX[d] * i;
|
||||
int sz = z + dirZ[d] * i;
|
||||
|
||||
// if this whole column is air, count it
|
||||
bool columnIsAir = true;
|
||||
for (int cy = placeY - 2; cy <= placeY + 2; cy++)
|
||||
{
|
||||
bottomY = yy;
|
||||
break;
|
||||
if (level->getTile(sx, cy, sz) != 0)
|
||||
{
|
||||
columnIsAir = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (columnIsAir)
|
||||
airCount++;
|
||||
}
|
||||
|
||||
if (airCount >= airThreshold)
|
||||
{
|
||||
nearEdge = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nearEdge) return false;
|
||||
|
||||
// bail out if there's already quicksoil nearby so we don't get clusters
|
||||
const int spacingRadius = 8;
|
||||
for (int sx = -spacingRadius; sx <= spacingRadius; sx++)
|
||||
{
|
||||
for (int sz = -spacingRadius; sz <= spacingRadius; sz++)
|
||||
{
|
||||
for (int sy = -3; sy <= 3; sy++)
|
||||
{
|
||||
if (level->getTile(x + sx, placeY + sy, z + sz) == Tile::quicksoil_Id)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bottomY < 1) return false;
|
||||
// place the shelf - flat quicksoil balcony at one Y level
|
||||
// grows outward each pass by attaching to existing blocks
|
||||
int shelfY = placeY;
|
||||
int radius = 3 + random->nextInt(4);
|
||||
int outwardPasses = 3 + random->nextInt(3);
|
||||
|
||||
// Generate a shelf of quicksoil on the underside of the island
|
||||
// Place quicksoil in a roughly circular blob hanging from the bottom
|
||||
int radius = 2 + random->nextInt(3); // radius 2-4
|
||||
int depth = 1 + random->nextInt(2); // depth 1-2
|
||||
|
||||
for (int dx = -radius; dx <= radius; dx++)
|
||||
for (int pass = 0; pass < outwardPasses; pass++)
|
||||
{
|
||||
for (int dz = -radius; dz <= radius; dz++)
|
||||
for (int dx = -radius; dx <= radius; dx++)
|
||||
{
|
||||
// Circular shape with some randomness
|
||||
float dist = sqrt((float)(dx * dx + dz * dz));
|
||||
if (dist > radius + 0.5f) continue;
|
||||
|
||||
// More likely to place at center, less at edges
|
||||
if (dist > radius - 1 && random->nextInt(3) != 0) continue;
|
||||
|
||||
int px = x + dx;
|
||||
int pz = z + dz;
|
||||
|
||||
for (int dy = 0; dy < depth; dy++)
|
||||
for (int dz = -radius; dz <= radius; dz++)
|
||||
{
|
||||
int py = bottomY - dy;
|
||||
if (py < 1) continue;
|
||||
float dist = sqrt((float)(dx * dx + dz * dz));
|
||||
if (dist > radius + 0.5f) continue;
|
||||
|
||||
int existing = level->getTile(px, py, pz);
|
||||
// Only replace air blocks or existing holystone/aetherDirt at the underside
|
||||
if (existing == 0 || existing == Tile::holystone_Id || existing == Tile::aetherDirt_Id)
|
||||
// thin out towards the edges
|
||||
if (dist > radius - 1 && random->nextInt(3) != 0) continue;
|
||||
|
||||
int px = x + dx;
|
||||
int pz = z + dz;
|
||||
|
||||
if (level->getTile(px, shelfY, pz) != 0) continue;
|
||||
|
||||
// only attach to adjacent solid blocks (sides + below, not above)
|
||||
bool canAttach = false;
|
||||
int hOffsets[][3] = { {1,0,0}, {-1,0,0}, {0,0,1}, {0,0,-1}, {0,-1,0} };
|
||||
for (int n = 0; n < 5; n++)
|
||||
{
|
||||
level->setTileNoUpdate(px, py, pz, Tile::quicksoil_Id);
|
||||
int neighbor = level->getTile(px + hOffsets[n][0], shelfY + hOffsets[n][1], pz + hOffsets[n][2]);
|
||||
if (isIslandBlock(neighbor) || neighbor == Tile::quicksoil_Id)
|
||||
{
|
||||
canAttach = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canAttach)
|
||||
{
|
||||
level->setTileNoUpdate(px, shelfY, pz, Tile::quicksoil_Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user