All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.level.generator.object.tree.ObjectCherryTree Maven / Gradle / Ivy

package cn.nukkit.level.generator.object.tree;

import cn.nukkit.api.PowerNukkitXOnly;
import cn.nukkit.api.Since;
import cn.nukkit.block.BlockCherryLeaves;
import cn.nukkit.block.BlockCherryLog;
import cn.nukkit.block.BlockID;
import cn.nukkit.blockstate.BlockState;
import cn.nukkit.level.ChunkManager;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.BlockVector3;
import cn.nukkit.math.NukkitRandom;
import cn.nukkit.math.Vector3;
import org.jetbrains.annotations.NotNull;

@Since("1.20.0-r2")
@PowerNukkitXOnly
public class ObjectCherryTree extends TreeGenerator {
    protected BlockState LOG_Y_AXIS;
    protected BlockState LOG_X_AXIS;
    protected BlockState LOG_Z_AXIS;
    protected BlockState LEAVES;

    public ObjectCherryTree() {
        var logY = new BlockCherryLog();
        logY.setPillarAxis(BlockFace.Axis.Y);
        this.LOG_Y_AXIS = logY.getCurrentState();
        var logX = new BlockCherryLog();
        logX.setPillarAxis(BlockFace.Axis.X);
        this.LOG_X_AXIS = logX.getCurrentState();
        var logZ = new BlockCherryLog();
        logZ.setPillarAxis(BlockFace.Axis.Z);
        this.LOG_Z_AXIS = logZ.getCurrentState();
        this.LEAVES = new BlockCherryLeaves().getCurrentState();
    }

    @Override
    public boolean generate(ChunkManager level, NukkitRandom rand, Vector3 position) {
        final int x = position.getFloorX();
        final int y = position.getFloorY();
        final int z = position.getFloorZ();

        final var isBigTree = rand.nextBoolean();
        if (isBigTree) {
            var ok = generateBigTree(level, rand, x, y, z);
            if (ok) return true;
        }
        return generateSmallTree(level, rand, x, y, z);
    }

    protected boolean generateBigTree(ChunkManager level, @NotNull NukkitRandom rand, final int x, final int y, final int z) {
        final int mainTrunkHeight = (rand.nextBoolean() ? 1 : 0) + 10;

        if (!canPlaceObject(level, mainTrunkHeight, x, y, z)) return false;

        var growOnXAxis = rand.nextBoolean();
        int xMultiplier = growOnXAxis ? 1 : 0;
        int zMultiplier = growOnXAxis ? 0 : 1;

        final int leftSideTrunkLength = rand.nextRange(2, 4);
        final int leftSideTrunkHeight = rand.nextRange(3, 5);
        final int leftSideTrunkStartY = rand.nextRange(4, 5);

        if (!canPlaceObject(level, leftSideTrunkHeight, x - leftSideTrunkLength * xMultiplier,
                y + leftSideTrunkStartY, z - leftSideTrunkLength * zMultiplier)) {
            growOnXAxis = !growOnXAxis;
            xMultiplier = growOnXAxis ? 1 : 0;
            zMultiplier = growOnXAxis ? 0 : 1;
            if (!canPlaceObject(level, leftSideTrunkHeight, x - leftSideTrunkLength * xMultiplier,
                    y + leftSideTrunkStartY, z - leftSideTrunkLength * zMultiplier)) {
                return false;
            }
        }

        final int rightSideTrunkLength = rand.nextRange(2, 4);
        final int rightSideTrunkHeight = rand.nextRange(3, 5);
        final int rightSideTrunkStartY = rand.nextRange(4, 5);

        if (!canPlaceObject(level, rightSideTrunkHeight, x + rightSideTrunkLength * xMultiplier,
                y + rightSideTrunkStartY, z + rightSideTrunkLength * zMultiplier)) return false;

        this.setDirtAt(level, new BlockVector3(x, y - 1, z));

        // Generate main trunk
        for (int yy = 0; yy < mainTrunkHeight; ++yy) {
            level.setBlockStateAt(x, y + yy, z, LOG_Y_AXIS);
        }
        // generate side trunks
        final var sideBlockState = growOnXAxis ? LOG_X_AXIS : LOG_Z_AXIS;
        // generate left-side trunk
        for (int xx = 1; xx <= leftSideTrunkLength; ++xx) {
            if (this.canGrowInto(level.getBlockIdAt(x - xx * xMultiplier, y + leftSideTrunkStartY, z - xx * zMultiplier)))
                level.setBlockStateAt(x - xx * xMultiplier, y + leftSideTrunkStartY, z - xx * zMultiplier, sideBlockState);
        }
        for (int yy = 1; yy < leftSideTrunkHeight; ++yy) {
            if (this.canGrowInto(level.getBlockIdAt(x - leftSideTrunkLength * xMultiplier,
                    y + leftSideTrunkStartY + yy, z - leftSideTrunkLength * zMultiplier)))
                level.setBlockStateAt(x - leftSideTrunkLength * xMultiplier, y + leftSideTrunkStartY + yy,
                        z - leftSideTrunkLength * zMultiplier, LOG_Y_AXIS);
        }
        // We just generated this above
        //       |
        // |     |     |
        // └-----|-----┘
        //       |
        // However, when start y == 4, minecraft generate trunk like this:
        //       |
        // └-┐   |   ┌-┘
        //   └---|---┘
        //       |
        if (leftSideTrunkStartY == 4) {
            var tmpX = x - leftSideTrunkLength * xMultiplier;
            var tmpY = y + leftSideTrunkStartY;
            var tmpZ = z - leftSideTrunkLength * zMultiplier;
            level.setBlockIdAt(tmpX, tmpY, tmpZ, 0);
            tmpX += xMultiplier;
            tmpY += 1;
            tmpZ += zMultiplier;
            if (this.canGrowInto(level.getBlockIdAt(tmpX, tmpY, tmpZ))) {
                level.setBlockStateAt(tmpX, tmpY, tmpZ, LOG_Y_AXIS);
            }
            tmpX -= xMultiplier;
            tmpZ -= zMultiplier;
            if (this.canGrowInto(level.getBlockIdAt(tmpX, tmpY, tmpZ))) {
                level.setBlockStateAt(tmpX, tmpY, tmpZ, sideBlockState);
            }
        }
        // generate right-side trunk
        for (int xx = 1; xx <= rightSideTrunkLength; ++xx) {
            if (this.canGrowInto(level.getBlockIdAt(x + xx * xMultiplier, y + rightSideTrunkStartY, z + xx * zMultiplier)))
                level.setBlockStateAt(x + xx * xMultiplier, y + rightSideTrunkStartY, z + xx * zMultiplier, sideBlockState);
        }
        for (int yy = 1; yy < rightSideTrunkHeight; ++yy) {
            if (this.canGrowInto(level.getBlockIdAt(x + rightSideTrunkLength * xMultiplier,
                    y + rightSideTrunkStartY + yy, z + rightSideTrunkLength * zMultiplier)))
                level.setBlockStateAt(x + rightSideTrunkLength * xMultiplier, y + rightSideTrunkStartY + yy,
                        z + rightSideTrunkLength * zMultiplier, LOG_Y_AXIS);
        }
        if (rightSideTrunkStartY == 4) {
            var tmpX = x + rightSideTrunkLength * xMultiplier;
            var tmpY = y + rightSideTrunkStartY;
            var tmpZ = z + rightSideTrunkLength * zMultiplier;
            level.setBlockIdAt(tmpX, tmpY, tmpZ, 0);
            tmpX -= xMultiplier;
            tmpY += 1;
            tmpZ -= zMultiplier;
            if (this.canGrowInto(level.getBlockIdAt(tmpX, tmpY, tmpZ))) {
                level.setBlockStateAt(tmpX, tmpY, tmpZ, LOG_Y_AXIS);
            }
            tmpX += xMultiplier;
            tmpZ += zMultiplier;
            if (this.canGrowInto(level.getBlockIdAt(tmpX, tmpY, tmpZ))) {
                level.setBlockStateAt(tmpX, tmpY, tmpZ, sideBlockState);
            }
        }
        // generate main trunk leaves
        generateLeaves(level, rand, x, y + mainTrunkHeight + 1, z);
        // generate left-side trunk leaves
        generateLeaves(level, rand, x - leftSideTrunkLength * xMultiplier,
                y + leftSideTrunkStartY + leftSideTrunkHeight + 1, z - leftSideTrunkLength * zMultiplier);
        // generate right-side trunk leaves
        generateLeaves(level, rand, x + rightSideTrunkLength * xMultiplier,
                y + rightSideTrunkStartY + rightSideTrunkHeight + 1, z + rightSideTrunkLength * zMultiplier);
        return true;
    }

    protected boolean generateSmallTree(ChunkManager level, @NotNull NukkitRandom rand, final int x, final int y, final int z) {
        final int mainTrunkHeight = (rand.nextBoolean() ? 1 : 0) + 4;
        final int sideTrunkHeight = rand.nextRange(3, 5);

        if (!canPlaceObject(level, mainTrunkHeight + 1, x, y, z)) return false;

        var growDirection = rand.nextRange(0, 3);
        int xMultiplier = 0;
        int zMultiplier = 0;
        var canPlace = false;
        for (int i = 0; i < 4; i++) {
            growDirection = (growDirection + 1) % 4;
            xMultiplier = switch (growDirection) {
                case 0 -> -1;
                case 1 -> 1;
                default -> 0;
            };
            zMultiplier = switch (growDirection) {
                case 2 -> -1;
                case 3 -> 1;
                default -> 0;
            };
            if (canPlaceObject(level, sideTrunkHeight, x + xMultiplier * sideTrunkHeight, y,
                    z + zMultiplier * sideTrunkHeight)) {
                canPlace = true;
                break;
            }
        }
        if (!canPlace) {
            return false;
        }


        final var sideBlockState = xMultiplier == 0 ? LOG_Z_AXIS : LOG_X_AXIS;
        // Generate main trunk
        for (int yy = 0; yy < mainTrunkHeight; ++yy) {
            if (this.canGrowInto(level.getBlockIdAt(x, y + yy, z)))
                level.setBlockStateAt(x, y + yy, z, LOG_Y_AXIS);
        }
        // Generate side trunk
        // (└)-┐      <- if side trunk is 4 or more blocks high, do not place the last block
        //     └-┐    <- side trunk
        //       └-┐
        //         |  <- main trunk
        //         |
        for (int yy = 1; yy <= sideTrunkHeight; ++yy) {
            var tmpX = x + yy * xMultiplier;
            var tmpY = y + mainTrunkHeight + yy - 2;
            var tmpZ = z + yy * zMultiplier;
            if (this.canGrowInto(level.getBlockIdAt(tmpX, tmpY, tmpZ))) {
                level.setBlockStateAt(tmpX, tmpY, tmpZ, sideBlockState);
            }
            // if side trunk is 4 or 5 blocks high, do not place the last block
            if (yy == sideTrunkHeight - 1 && sideTrunkHeight > 3) {
                continue;
            }
            tmpY += 1;
            if (this.canGrowInto(level.getBlockIdAt(tmpX, tmpY, tmpZ))) {
                level.setBlockStateAt(tmpX, tmpY, tmpZ, LOG_Y_AXIS);
            }
        }

        // generate leaves
        generateLeaves(level, rand, x + sideTrunkHeight * xMultiplier, y + mainTrunkHeight + sideTrunkHeight,
                z + sideTrunkHeight * zMultiplier);

        return true;
    }

    static final int LEAVES_RADIUS = 4;

    public void generateLeaves(ChunkManager level, NukkitRandom rand, final int x, final int y, final int z) {
        for (int dy = -2; dy <= 2; dy++) {
            for (int dx = -LEAVES_RADIUS; dx <= LEAVES_RADIUS; dx++) {
                for (int dz = -LEAVES_RADIUS; dz <= LEAVES_RADIUS; dz++) {
                    var currentRadius = LEAVES_RADIUS - (Math.max(1, Math.abs(dy)));
                    if (dx * dx + dz * dz > currentRadius * currentRadius) continue;
                    var blockId = level.getBlockIdAt(x + dx, y + dy, z + dz);
                    if (blockId == 0 || blockId == BlockID.LEAVES || blockId == BlockID.LEAVES2 ||
                            blockId == BlockID.AZALEA_LEAVES || blockId == BlockID.AZALEA_LEAVES_FLOWERED) {
                        level.setBlockStateAt(x + dx, y + dy, z + dz, LEAVES);
                    }
                    if (dy == -2 && rand.nextRange(0, 2) == 0) {
                        blockId = level.getBlockIdAt(x + dx, y + dy - 1, z + dz);
                        if (blockId == 0 || blockId == BlockID.LEAVES || blockId == BlockID.LEAVES2 ||
                                blockId == BlockID.AZALEA_LEAVES || blockId == BlockID.AZALEA_LEAVES_FLOWERED) {
                            level.setBlockStateAt(x + dx, y + dy - 1, z + dz, LEAVES);
                        }
                    }
                }
            }
        }
    }

    public boolean canPlaceObject(ChunkManager level, int treeHeight, int x, int y, int z) {
        int radiusToCheck = 0;
        for (int yy = 0; yy < treeHeight + 3; ++yy) {
            if (yy == 1 || yy == treeHeight) {
                ++radiusToCheck;
            }
            for (int xx = -radiusToCheck; xx < (radiusToCheck + 1); ++xx) {
                for (int zz = -radiusToCheck; zz < (radiusToCheck + 1); ++zz) {
                    if (!this.canGrowInto(level.getBlockIdAt(x + xx, y + yy, z + zz))) {
                        return false;
                    }
                }
            }
        }

        return true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy