mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-05-22 04:46:07 +00:00
218 lines
6.8 KiB
C++
218 lines
6.8 KiB
C++
#include "stdafx.h"
|
|
#include "MegaPineTreeFeature.h"
|
|
#include "net.minecraft.world.level.h"
|
|
#include "net.minecraft.world.level.tile.h"
|
|
#include "Random.h"
|
|
#include "Level.h"
|
|
|
|
MegaPineTreeFeature::MegaPineTreeFeature(bool doUpdate, bool useBaseHeight) : Feature(doUpdate), useBaseHeight(useBaseHeight)
|
|
{
|
|
}
|
|
|
|
// Port of WorldGenHugeTrees.func_175925_a
|
|
// Places a layer of leaves centered on the 2x2 trunk at (x,z),(x+1,z),(x,z+1),(x+1,z+1)
|
|
// Equivalent to: keep block if it's within `radius` of at least one of the 4 trunk corners.
|
|
void MegaPineTreeFeature::placeLeavesLayer(Level *level, int x, int z, int y, int radius, Random *random)
|
|
{
|
|
int rSq = radius * radius;
|
|
// Loop from x-radius to x+radius+1 (extra +1 for 2x2 trunk width)
|
|
for (int xx = x - radius; xx <= x + radius + 1; xx++)
|
|
{
|
|
for (int zz = z - radius; zz <= z + radius + 1; zz++)
|
|
{
|
|
// Distance from each of the 4 trunk corner columns
|
|
int j0 = xx - x, k0 = zz - z;
|
|
int j1 = xx - (x+1), k1 = zz - z;
|
|
int j2 = xx - x, k2 = zz - (z+1);
|
|
int j3 = xx - (x+1), k3 = zz - (z+1);
|
|
// Place leaf if within radius of ANY trunk corner
|
|
if (j0*j0 + k0*k0 > rSq &&
|
|
j1*j1 + k1*k1 > rSq &&
|
|
j2*j2 + k2*k2 > rSq &&
|
|
j3*j3 + k3*k3 > rSq)
|
|
{
|
|
continue;
|
|
}
|
|
// Java: only place on air or leaf blocks (isAirLeaves equivalent)
|
|
int t = level->getTile(xx, y, zz);
|
|
if (t == 0 || t == Tile::leaves_Id || t == Tile::leaves2_Id)
|
|
{
|
|
placeBlock(level, xx, y, zz, Tile::leaves_Id, LeafTile::EVERGREEN_LEAF);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Port of WorldGenMegaPineTree.func_150541_c
|
|
// Generates conical leaf crown from the top of the tree downward.
|
|
// p_150541_5_ is always 0 in the original call (base radius offset)
|
|
void MegaPineTreeFeature::placeCrown(Level *level, int x, int z, int treeTop, Random *random)
|
|
{
|
|
// Crown span = rand.nextInt(5) + (useBaseHeight ? baseHeight=13 : 3)
|
|
int crownHeight = random->nextInt(5) + (this->useBaseHeight ? 13 : 3);
|
|
int prevRadius = 0;
|
|
|
|
// k goes from (treeTop - crownHeight) to treeTop
|
|
for (int k = treeTop - crownHeight; k <= treeTop; k++)
|
|
{
|
|
int l = treeTop - k; // l = distance from top: goes from crownHeight down to 0
|
|
int i1 = (int)floorf((float)l / (float)crownHeight * 3.5f); // base radius at layer
|
|
// Stagger: if same radius as layer below and even Y, expand by 1
|
|
int layerRadius = i1 + (l > 0 && i1 == prevRadius && (k & 1) == 0 ? 1 : 0);
|
|
placeLeavesLayer(level, x, z, k, layerRadius, random);
|
|
prevRadius = i1;
|
|
}
|
|
}
|
|
|
|
// Port of WorldGenMegaPineTree.func_175934_c
|
|
// Searches downward from y+2 to y-3 and replaces first grass/dirt with Podzol
|
|
void MegaPineTreeFeature::func_175934_c(Level *level, int x, int z, int y)
|
|
{
|
|
for (int i = 2; i >= -3; --i)
|
|
{
|
|
int targetY = y + i;
|
|
int tile = level->getTile(x, targetY, z);
|
|
// canSustainPlant equivalent: grass or dirt
|
|
if (tile == Tile::grass_Id || tile == Tile::dirt_Id)
|
|
{
|
|
placeBlock(level, x, targetY, z, Tile::dirt_Id, DirtTile::PODZOL);
|
|
break;
|
|
}
|
|
if (tile != 0 && i < 0) break;
|
|
}
|
|
}
|
|
|
|
// Port of WorldGenMegaPineTree.func_175933_b
|
|
// Spreads Podzol in a 5x5 (minus corners) pattern centered at (x,z)
|
|
void MegaPineTreeFeature::func_175933_b(Level *level, int x, int z, int y)
|
|
{
|
|
for (int i = -2; i <= 2; ++i)
|
|
{
|
|
for (int j = -2; j <= 2; ++j)
|
|
{
|
|
if (abs(i) != 2 || abs(j) != 2) // skip the 4 far corners
|
|
{
|
|
func_175934_c(level, x + i, z + j, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Port of WorldGenMegaPineTree.func_180711_a
|
|
// Places Podzol patches at the 4 corners + 5 random border positions
|
|
void MegaPineTreeFeature::placeBase(Level *level, Random *random, int x, int z, int y)
|
|
{
|
|
// 4 fixed corners (relative to 2x2 trunk)
|
|
func_175933_b(level, x - 1, z - 1, y); // west().north()
|
|
func_175933_b(level, x + 2, z - 1, y); // east(2).north()
|
|
func_175933_b(level, x - 1, z + 2, y); // west().south(2)
|
|
func_175933_b(level, x + 2, z + 2, y); // east(2).south(2)
|
|
|
|
// 5 random border positions (8x8 grid, only border cells)
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
int j = random->nextInt(64);
|
|
int k = j % 8;
|
|
int l = j / 8;
|
|
if (k == 0 || k == 7 || l == 0 || l == 7)
|
|
{
|
|
func_175933_b(level, x - 3 + k, z - 3 + l, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Port of WorldGenHugeTrees.func_175929_a (space check)
|
|
// Returns true if the tree can grow: checks all blocks from y to y+height+1
|
|
bool MegaPineTreeFeature::canPlace(Level *level, Random *random, int x, int y, int z, int height)
|
|
{
|
|
if (y < 1 || y + height + 1 > Level::maxBuildHeight) return false;
|
|
|
|
for (int l = 0; l <= height + 1; l++)
|
|
{
|
|
int r = (l == 0) ? 1 : 2; // bottom layer: radius 1; rest: radius 2
|
|
int yy = y + l;
|
|
for (int xx = x - r; xx <= x + r + 1; xx++)
|
|
{
|
|
for (int zz = z - r; zz <= z + r + 1; zz++)
|
|
{
|
|
if (yy < 0 || yy >= Level::maxBuildHeight) return false;
|
|
int t = level->getTile(xx, yy, zz);
|
|
if (!isReplaceable(t)) return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ground check: all 4 blocks below the 2x2 trunk must be grass or dirt
|
|
auto isGround = [&](int bx, int bz) {
|
|
int t = level->getTile(bx, y - 1, bz);
|
|
return t == Tile::grass_Id || t == Tile::dirt_Id;
|
|
};
|
|
return isGround(x, z) && isGround(x+1, z) && isGround(x, z+1) && isGround(x+1, z+1);
|
|
}
|
|
|
|
bool MegaPineTreeFeature::isAirLeaves(Level *level, int x, int y, int z)
|
|
{
|
|
int t = level->getTile(x, y, z);
|
|
return t == 0 || t == Tile::leaves_Id || t == Tile::leaves2_Id;
|
|
}
|
|
|
|
// Equivalent to Java's canGrowInto: blocks the tree can grow through
|
|
bool MegaPineTreeFeature::isReplaceable(int tileId)
|
|
{
|
|
return tileId == 0
|
|
|| tileId == Tile::leaves_Id
|
|
|| tileId == Tile::leaves2_Id
|
|
|| tileId == Tile::grass_Id
|
|
|| tileId == Tile::dirt_Id
|
|
|| tileId == Tile::treeTrunk_Id
|
|
|| tileId == Tile::sapling_Id
|
|
|| tileId == Tile::deadBush_Id
|
|
|| tileId == Tile::tallgrass_Id
|
|
|| tileId == Tile::snow_Id;
|
|
}
|
|
|
|
bool MegaPineTreeFeature::place(Level *level, Random *random, int x, int y, int z)
|
|
{
|
|
// func_150533_a: rand.nextInt(maxVariation - baseHeight) + baseHeight
|
|
// = rand.nextInt(15 - 13) + 13 = rand.nextInt(2) + 13
|
|
int height = random->nextInt(2) + 13;
|
|
|
|
// func_175929_a: space check
|
|
if (!canPlace(level, random, x, y, z, height))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// func_150541_c: generate conical leaf crown first
|
|
placeCrown(level, x, z, y + height, random);
|
|
|
|
// Trunk: 2x2 column of spruce logs
|
|
for (int j = 0; j < height; j++)
|
|
{
|
|
if (isAirLeaves(level, x, y + j, z))
|
|
{
|
|
placeBlock(level, x, y + j, z, Tile::treeTrunk_Id, TreeTile::SPRUCE_TRUNK);
|
|
}
|
|
if (j < height - 1) // 3 extra columns stop 1 short of the top
|
|
{
|
|
if (isAirLeaves(level, x + 1, y + j, z))
|
|
{
|
|
placeBlock(level, x + 1, y + j, z, Tile::treeTrunk_Id, TreeTile::SPRUCE_TRUNK);
|
|
}
|
|
if (isAirLeaves(level, x + 1, y + j, z + 1))
|
|
{
|
|
placeBlock(level, x + 1, y + j, z + 1, Tile::treeTrunk_Id, TreeTile::SPRUCE_TRUNK);
|
|
}
|
|
if (isAirLeaves(level, x, y + j, z + 1))
|
|
{
|
|
placeBlock(level, x, y + j, z + 1, Tile::treeTrunk_Id, TreeTile::SPRUCE_TRUNK);
|
|
}
|
|
}
|
|
}
|
|
|
|
// func_180711_a: generate Podzol base patches
|
|
placeBase(level, random, x, z, y);
|
|
|
|
return true;
|
|
}
|