Cactus fixes & enhancements

This commit is contained in:
paulevsGitch 2021-04-28 01:13:15 +03:00
parent bd73d1c576
commit cce934d537
12 changed files with 507 additions and 375 deletions

View file

@ -1,304 +1,11 @@
package ru.betterend.blocks;
import java.util.EnumMap;
import java.util.Random;
import com.google.common.collect.Maps;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import ru.betterend.blocks.BlockProperties.CactusBottom;
import ru.betterend.blocks.BlockProperties.TripleShape;
import ru.betterend.blocks.basis.BlockBaseNotFull;
import ru.betterend.client.render.ERenderLayer;
import ru.betterend.interfaces.IRenderTypeable;
import ru.betterend.registry.EndBlocks;
import ru.betterend.registry.EndTags;
import ru.betterend.util.BlocksHelper;
import ru.betterend.util.MHelper;
public class NeonCactusBlock extends BlockBaseNotFull implements SimpleWaterloggedBlock, IRenderTypeable {
public static final EnumProperty<TripleShape> SHAPE = BlockProperties.TRIPLE_SHAPE;
public static final EnumProperty<CactusBottom> CACTUS_BOTTOM = BlockProperties.CACTUS_BOTTOM;
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public static final DirectionProperty FACING = BlockStateProperties.FACING;
private static final EnumMap<Direction, VoxelShape> BIG_SHAPES_OPEN = Maps.newEnumMap(Direction.class);
private static final EnumMap<Direction, VoxelShape> MEDIUM_SHAPES_OPEN = Maps.newEnumMap(Direction.class);
private static final EnumMap<Direction, VoxelShape> SMALL_SHAPES_OPEN = Maps.newEnumMap(Direction.class);
private static final EnumMap<Axis, VoxelShape> BIG_SHAPES = Maps.newEnumMap(Axis.class);
private static final EnumMap<Axis, VoxelShape> MEDIUM_SHAPES = Maps.newEnumMap(Axis.class);
private static final EnumMap<Axis, VoxelShape> SMALL_SHAPES = Maps.newEnumMap(Axis.class);
private static final int MAX_LENGTH = 12;
import ru.betterend.blocks.basis.EndPillarBlock;
public class NeonCactusBlock extends EndPillarBlock {
public NeonCactusBlock() {
super(FabricBlockSettings.copyOf(Blocks.CACTUS).luminance(15).randomTicks());
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false).setValue(FACING, Direction.UP).setValue(SHAPE, TripleShape.TOP));
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> stateManager) {
stateManager.add(SHAPE, CACTUS_BOTTOM, WATERLOGGED, FACING);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
LevelAccessor world = ctx.getLevel();
BlockPos pos = ctx.getClickedPos();
Direction dir = ctx.getClickedFace();
BlockState down = world.getBlockState(pos.relative(dir.getOpposite()));
BlockState state = this.defaultBlockState().setValue(WATERLOGGED, world.getFluidState(pos).getType() == Fluids.WATER).setValue(FACING, ctx.getClickedFace());
if (down.is(Blocks.END_STONE) || down.is(EndBlocks.ENDSTONE_DUST)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.SAND);
}
else if (down.is(EndBlocks.END_MOSS)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.MOSS);
}
else {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.EMPTY);
}
return state;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return BlocksHelper.rotateHorizontal(state, rotation, FACING);
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return BlocksHelper.mirrorHorizontal(state, mirror, FACING);
}
@Override
public FluidState getFluidState(BlockState state) {
return (Boolean) state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState newState, LevelAccessor world, BlockPos pos, BlockPos posFrom) {
if (!canSurvive(state, world, pos)) {
world.getBlockTicks().scheduleTick(pos, this, MHelper.randRange(1, 4, world.getRandom()));
return state;
}
if ((Boolean) state.getValue(WATERLOGGED)) {
world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
}
Direction dir = state.getValue(FACING);
BlockState downState = world.getBlockState(pos.relative(dir.getOpposite()));
if (downState.is(Blocks.END_STONE) || downState.is(EndBlocks.ENDSTONE_DUST)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.SAND);
}
else if (downState.is(EndBlocks.END_MOSS)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.MOSS);
}
else {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.EMPTY);
}
return state;
}
@Override
public void tick(BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, Random random) {
if (!blockState.canSurvive(serverLevel, blockPos)) {
serverLevel.destroyBlock(blockPos, true);
}
}
@Override
public ERenderLayer getRenderLayer() {
return ERenderLayer.CUTOUT;
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) {
TripleShape shape = state.getValue(SHAPE);
Direction dir = state.getValue(FACING);
BlockState next = view.getBlockState(pos.relative(dir));
if (next.is(this)) {
Axis axis = dir.getAxis();
if (shape == TripleShape.BOTTOM) {
return BIG_SHAPES.get(axis);
}
return shape == TripleShape.MIDDLE ? MEDIUM_SHAPES.get(axis) : SMALL_SHAPES.get(axis);
}
else {
if (shape == TripleShape.BOTTOM) {
return BIG_SHAPES_OPEN.get(dir);
}
return shape == TripleShape.MIDDLE ? MEDIUM_SHAPES_OPEN.get(dir) : SMALL_SHAPES_OPEN.get(dir);
}
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
Direction dir = state.getValue(FACING);
BlockPos supportPos = pos.relative(dir.getOpposite());
BlockState support = level.getBlockState(supportPos);
return support.is(this) || support.isFaceSturdy(level, supportPos, dir);
}
@Override
public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) {
if (!this.canSurvive(state, world, pos)) {
return;
}
Direction dir = state.getValue(FACING);
if (!world.isEmptyBlock(pos.relative(dir))) {
return;
}
int length = getLength(state, world, pos, MAX_LENGTH);
if (length < 0 || length > MAX_LENGTH - 1) {
return;
}
if (dir != Direction.UP) {
int horizontal = getHorizontal(state, world, pos, 2);
if (horizontal > random.nextInt(2)) {
dir = Direction.UP;
if (!world.getBlockState(pos.above()).isAir()) {
return;
}
}
}
else if (length > 1 && world.getBlockState(pos.below()).is(this)) {
Direction side = BlocksHelper.randomHorizontal(random);
BlockPos sidePos = pos.relative(side);
if (world.isEmptyBlock(sidePos)) {
BlockState placement = state.setValue(SHAPE, TripleShape.TOP).setValue(CACTUS_BOTTOM, CactusBottom.EMPTY).setValue(WATERLOGGED, false).setValue(FACING, side);
BlocksHelper.setWithoutUpdate(world, sidePos, placement);
}
}
BlockState placement = state.setValue(SHAPE, TripleShape.TOP).setValue(CACTUS_BOTTOM, CactusBottom.EMPTY).setValue(WATERLOGGED, false).setValue(FACING, dir);
BlocksHelper.setWithoutUpdate(world, pos.relative(dir), placement);
mutateStem(placement, world, pos, MAX_LENGTH);
}
@Override
public boolean isPathfindable(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, PathComputationType pathComputationType) {
return false;
}
@Override
public void entityInside(BlockState blockState, Level level, BlockPos blockPos, Entity entity) {
entity.hurt(DamageSource.CACTUS, 1.0F);
}
private int getLength(BlockState state, ServerLevel world, BlockPos pos, int max) {
int length = 0;
Direction dir = state.getValue(FACING).getOpposite();
MutableBlockPos mut = new MutableBlockPos().set(pos);
for (int i = 0; i < max; i++) {
mut.move(dir);
state = world.getBlockState(mut);
if (!state.is(this)) {
if (!state.is(EndTags.END_GROUND)) {
length = -1;
}
break;
}
dir = state.getValue(FACING).getOpposite();
length ++;
}
return length;
}
private int getHorizontal(BlockState state, ServerLevel world, BlockPos pos, int max) {
int count = 0;
Direction dir = state.getValue(FACING).getOpposite();
MutableBlockPos mut = new MutableBlockPos().set(pos);
for (int i = 0; i < max; i++) {
mut.move(dir);
state = world.getBlockState(mut);
if (!state.is(this)) {
break;
}
dir = state.getValue(FACING).getOpposite();
if (dir.getStepY() != 0) {
break;
}
count ++;
}
return count;
}
private void mutateStem(BlockState state, ServerLevel world, BlockPos pos, int max) {
Direction dir = state.getValue(FACING).getOpposite();
MutableBlockPos mut = new MutableBlockPos().set(pos);
for (int i = 0; i < max; i++) {
mut.move(dir);
state = world.getBlockState(mut);
if (!state.is(this)) {
return;
}
int size = (i + 2) * 3 / max;
int src = state.getValue(SHAPE).getIndex();
dir = state.getValue(FACING).getOpposite();
if (src < size) {
TripleShape shape = TripleShape.fromIndex(size);
BlocksHelper.setWithoutUpdate(world, mut, state.setValue(SHAPE, shape));
}
}
}
static {
BIG_SHAPES.put(Axis.X, Block.box(0, 2, 2, 16, 14, 14));
BIG_SHAPES.put(Axis.Y, Block.box(2, 0, 2, 14, 16, 14));
BIG_SHAPES.put(Axis.Z, Block.box(2, 2, 0, 14, 14, 16));
MEDIUM_SHAPES.put(Axis.X, Block.box(0, 3, 3, 16, 13, 13));
MEDIUM_SHAPES.put(Axis.Y, Block.box(3, 0, 3, 13, 16, 13));
MEDIUM_SHAPES.put(Axis.Z, Block.box(3, 3, 0, 13, 13, 16));
SMALL_SHAPES.put(Axis.X, Block.box(0, 4, 4, 16, 12, 12));
SMALL_SHAPES.put(Axis.Y, Block.box(4, 0, 4, 12, 16, 12));
SMALL_SHAPES.put(Axis.Z, Block.box(4, 4, 0, 12, 12, 16));
BIG_SHAPES_OPEN.put(Direction.UP, Block.box(2, 0, 2, 14, 14, 14));
BIG_SHAPES_OPEN.put(Direction.DOWN, Block.box(2, 2, 2, 14, 16, 14));
BIG_SHAPES_OPEN.put(Direction.NORTH, Block.box(2, 2, 2, 14, 14, 16));
BIG_SHAPES_OPEN.put(Direction.SOUTH, Block.box(2, 2, 0, 14, 14, 14));
BIG_SHAPES_OPEN.put(Direction.WEST, Block.box(2, 2, 2, 16, 14, 14));
BIG_SHAPES_OPEN.put(Direction.EAST, Block.box(0, 2, 2, 14, 14, 14));
MEDIUM_SHAPES_OPEN.put(Direction.UP, Block.box(3, 0, 3, 13, 13, 13));
MEDIUM_SHAPES_OPEN.put(Direction.DOWN, Block.box(3, 3, 3, 13, 16, 13));
MEDIUM_SHAPES_OPEN.put(Direction.NORTH, Block.box(3, 3, 3, 13, 13, 16));
MEDIUM_SHAPES_OPEN.put(Direction.SOUTH, Block.box(3, 3, 0, 13, 13, 13));
MEDIUM_SHAPES_OPEN.put(Direction.WEST, Block.box(3, 3, 3, 16, 13, 13));
MEDIUM_SHAPES_OPEN.put(Direction.EAST, Block.box(0, 3, 3, 13, 13, 13));
SMALL_SHAPES_OPEN.put(Direction.UP, Block.box(4, 0, 4, 12, 12, 12));
SMALL_SHAPES_OPEN.put(Direction.DOWN, Block.box(4, 4, 4, 12, 16, 12));
SMALL_SHAPES_OPEN.put(Direction.NORTH, Block.box(4, 4, 4, 12, 12, 16));
SMALL_SHAPES_OPEN.put(Direction.SOUTH, Block.box(4, 4, 0, 12, 12, 12));
SMALL_SHAPES_OPEN.put(Direction.WEST, Block.box(4, 4, 4, 16, 12, 12));
SMALL_SHAPES_OPEN.put(Direction.EAST, Block.box(0, 4, 4, 12, 12, 12));
super(FabricBlockSettings.copyOf(Blocks.CACTUS).luminance(15));
}
}

View file

@ -0,0 +1,396 @@
package ru.betterend.blocks;
import java.util.EnumMap;
import java.util.List;
import java.util.Random;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import ru.betterend.blocks.BlockProperties.CactusBottom;
import ru.betterend.blocks.BlockProperties.TripleShape;
import ru.betterend.blocks.basis.BlockBaseNotFull;
import ru.betterend.client.render.ERenderLayer;
import ru.betterend.interfaces.IRenderTypeable;
import ru.betterend.registry.EndBlocks;
import ru.betterend.registry.EndTags;
import ru.betterend.util.BlocksHelper;
import ru.betterend.util.MHelper;
public class NeonCactusPlantBlock extends BlockBaseNotFull implements SimpleWaterloggedBlock, IRenderTypeable {
public static final EnumProperty<TripleShape> SHAPE = BlockProperties.TRIPLE_SHAPE;
public static final EnumProperty<CactusBottom> CACTUS_BOTTOM = BlockProperties.CACTUS_BOTTOM;
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public static final DirectionProperty FACING = BlockStateProperties.FACING;
private static final EnumMap<Direction, VoxelShape> BIG_SHAPES_OPEN = Maps.newEnumMap(Direction.class);
private static final EnumMap<Direction, VoxelShape> MEDIUM_SHAPES_OPEN = Maps.newEnumMap(Direction.class);
private static final EnumMap<Direction, VoxelShape> SMALL_SHAPES_OPEN = Maps.newEnumMap(Direction.class);
private static final EnumMap<Axis, VoxelShape> BIG_SHAPES = Maps.newEnumMap(Axis.class);
private static final EnumMap<Axis, VoxelShape> MEDIUM_SHAPES = Maps.newEnumMap(Axis.class);
private static final EnumMap<Axis, VoxelShape> SMALL_SHAPES = Maps.newEnumMap(Axis.class);
private static final int MAX_LENGTH = 12;
public NeonCactusPlantBlock() {
super(FabricBlockSettings.copyOf(Blocks.CACTUS).luminance(15).randomTicks());
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false).setValue(FACING, Direction.UP).setValue(SHAPE, TripleShape.TOP));
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> stateManager) {
stateManager.add(SHAPE, CACTUS_BOTTOM, WATERLOGGED, FACING);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
LevelAccessor world = ctx.getLevel();
BlockPos pos = ctx.getClickedPos();
Direction dir = ctx.getClickedFace();
BlockState down = world.getBlockState(pos.relative(dir.getOpposite()));
BlockState state = this.defaultBlockState().setValue(WATERLOGGED, world.getFluidState(pos).getType() == Fluids.WATER).setValue(FACING, ctx.getClickedFace());
if (down.is(Blocks.END_STONE) || down.is(EndBlocks.ENDSTONE_DUST)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.SAND);
}
else if (down.is(EndBlocks.END_MOSS)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.MOSS);
}
else {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.EMPTY);
}
return state;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return BlocksHelper.rotateHorizontal(state, rotation, FACING);
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return BlocksHelper.mirrorHorizontal(state, mirror, FACING);
}
@Override
public FluidState getFluidState(BlockState state) {
return (Boolean) state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState newState, LevelAccessor world, BlockPos pos, BlockPos posFrom) {
if (!canSurvive(state, world, pos)) {
world.getBlockTicks().scheduleTick(pos, this, MHelper.randRange(1, 4, world.getRandom()));
return state;
}
if ((Boolean) state.getValue(WATERLOGGED)) {
world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
}
Direction dir = state.getValue(FACING);
BlockState downState = world.getBlockState(pos.relative(dir.getOpposite()));
if (downState.is(Blocks.END_STONE) || downState.is(EndBlocks.ENDSTONE_DUST)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.SAND);
}
else if (downState.is(EndBlocks.END_MOSS)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.MOSS);
}
else {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.EMPTY);
}
return state;
}
@Override
public void tick(BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, Random random) {
if (!blockState.canSurvive(serverLevel, blockPos)) {
serverLevel.destroyBlock(blockPos, true);
}
}
@Override
public ERenderLayer getRenderLayer() {
return ERenderLayer.CUTOUT;
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) {
TripleShape shape = state.getValue(SHAPE);
Direction dir = state.getValue(FACING);
BlockState next = view.getBlockState(pos.relative(dir));
if (next.is(this)) {
Axis axis = dir.getAxis();
if (shape == TripleShape.BOTTOM) {
return BIG_SHAPES.get(axis);
}
return shape == TripleShape.MIDDLE ? MEDIUM_SHAPES.get(axis) : SMALL_SHAPES.get(axis);
}
else {
if (shape == TripleShape.BOTTOM) {
return BIG_SHAPES_OPEN.get(dir);
}
return shape == TripleShape.MIDDLE ? MEDIUM_SHAPES_OPEN.get(dir) : SMALL_SHAPES_OPEN.get(dir);
}
}
@Override
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
Direction dir = state.getValue(FACING);
BlockPos supportPos = pos.relative(dir.getOpposite());
BlockState support = level.getBlockState(supportPos);
return support.is(this) || support.isFaceSturdy(level, supportPos, dir);
}
@Override
public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) {
if (!this.canSurvive(state, world, pos) || random.nextInt(8) > 0) {
return;
}
Direction dir = state.getValue(FACING);
if (!world.isEmptyBlock(pos.relative(dir))) {
return;
}
int length = getLength(state, world, pos, MAX_LENGTH);
if (length < 0 || length > MAX_LENGTH - 1) {
return;
}
if (dir.getAxis().isHorizontal()) {
int horizontal = getHorizontal(state, world, pos, 2);
if (horizontal > random.nextInt(2)) {
dir = Direction.UP;
if (!world.getBlockState(pos.above()).isAir()) {
return;
}
}
}
else if (length > 1 && world.getBlockState(pos.below()).is(this)) {
Direction side = BlocksHelper.randomHorizontal(random);
BlockPos sidePos = pos.relative(side);
if (world.isEmptyBlock(sidePos)) {
BlockState placement = state.setValue(SHAPE, TripleShape.TOP).setValue(CACTUS_BOTTOM, CactusBottom.EMPTY).setValue(WATERLOGGED, false).setValue(FACING, side);
BlocksHelper.setWithoutUpdate(world, sidePos, placement);
}
}
BlockState placement = state.setValue(SHAPE, TripleShape.TOP).setValue(CACTUS_BOTTOM, CactusBottom.EMPTY).setValue(WATERLOGGED, false).setValue(FACING, dir);
BlocksHelper.setWithoutUpdate(world, pos.relative(dir), placement);
mutateStem(placement, world, pos, MAX_LENGTH);
}
public void growPlant(WorldGenLevel world, BlockPos pos, Random random) {
growPlant(world, pos, random, MHelper.randRange(MAX_LENGTH >> 1, MAX_LENGTH, random));
}
public void growPlant(WorldGenLevel world, BlockPos pos, Random random, int iterations) {
BlockState state = defaultBlockState();
BlockState downState = world.getBlockState(pos.below());
if (downState.is(Blocks.END_STONE) || downState.is(EndBlocks.ENDSTONE_DUST)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.SAND);
}
else if (downState.is(EndBlocks.END_MOSS)) {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.MOSS);
}
else {
state = state.setValue(CACTUS_BOTTOM, CactusBottom.EMPTY);
}
BlocksHelper.setWithoutUpdate(world, pos, state);
List<MutableBlockPos> ends = Lists.newArrayList(pos.mutable());
for (int i = 0; i < iterations; i++) {
int count = ends.size();
for (int n = 0; n < count; n++) {
if (!growIteration(world, ends.get(n), random, ends, i)) {
ends.remove(n);
count--;
n--;
}
}
}
}
private boolean growIteration(WorldGenLevel world, MutableBlockPos pos, Random random, List<MutableBlockPos> ends, int length) {
BlockState state = world.getBlockState(pos);
if (!state.is(this)) {
return false;
}
Direction dir = state.getValue(FACING);
if (!world.isEmptyBlock(pos.relative(dir))) {
return false;
}
if (dir.getAxis().isHorizontal()) {
int horizontal = getHorizontal(state, world, pos, 2);
if (horizontal > random.nextInt(2)) {
dir = Direction.UP;
if (!world.getBlockState(pos.above()).isAir()) {
return false;
}
}
}
else if (length > 1 && world.getBlockState(pos.below()).is(this)) {
MutableBlockPos iterPos = pos.mutable();
BlockState iterState = state;
Direction startDir = null;
Direction lastDir = null;
while (iterState.is(this)) {
startDir = iterState.getValue(FACING);
if (lastDir == null) {
for (Direction side: BlocksHelper.HORIZONTAL) {
BlockState sideState = world.getBlockState(iterPos.relative(side));
if (sideState.is(this)) {
Direction sideDir = sideState.getValue(FACING);
if (sideDir != side) {
continue;
}
lastDir = sideDir;
}
}
}
iterPos.move(dir);
iterState = world.getBlockState(iterPos);
}
Direction side = lastDir == null ? BlocksHelper.randomHorizontal(random) : lastDir.getClockWise();
if (side.getOpposite() == startDir) {
side = side.getClockWise();
}
BlockPos sidePos = pos.relative(side);
if (world.isEmptyBlock(sidePos)) {
BlockState placement = state.setValue(SHAPE, TripleShape.TOP).setValue(CACTUS_BOTTOM, CactusBottom.EMPTY).setValue(WATERLOGGED, false).setValue(FACING, side);
BlocksHelper.setWithoutUpdate(world, sidePos, placement);
ends.add(sidePos.mutable());
}
}
BlockState placement = state.setValue(SHAPE, TripleShape.TOP).setValue(CACTUS_BOTTOM, CactusBottom.EMPTY).setValue(WATERLOGGED, false).setValue(FACING, dir);
BlocksHelper.setWithoutUpdate(world, pos.relative(dir), placement);
mutateStem(placement, world, pos, MAX_LENGTH);
pos.move(dir);
return true;
}
@Override
public boolean isPathfindable(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, PathComputationType pathComputationType) {
return false;
}
@Override
public void entityInside(BlockState blockState, Level level, BlockPos blockPos, Entity entity) {
entity.hurt(DamageSource.CACTUS, 1.0F);
}
private int getLength(BlockState state, ServerLevel world, BlockPos pos, int max) {
int length = 0;
Direction dir = state.getValue(FACING).getOpposite();
MutableBlockPos mut = new MutableBlockPos().set(pos);
for (int i = 0; i < max; i++) {
mut.move(dir);
state = world.getBlockState(mut);
if (!state.is(this)) {
if (!state.is(EndTags.END_GROUND)) {
length = -1;
}
break;
}
dir = state.getValue(FACING).getOpposite();
length ++;
}
return length;
}
private int getHorizontal(BlockState state, WorldGenLevel world, BlockPos pos, int max) {
int count = 0;
Direction dir = state.getValue(FACING).getOpposite();
MutableBlockPos mut = new MutableBlockPos().set(pos);
for (int i = 0; i < max; i++) {
mut.move(dir);
state = world.getBlockState(mut);
if (!state.is(this)) {
break;
}
dir = state.getValue(FACING).getOpposite();
if (dir.getStepY() != 0) {
break;
}
count ++;
}
return count;
}
private void mutateStem(BlockState state, WorldGenLevel world, BlockPos pos, int max) {
Direction dir = state.getValue(FACING).getOpposite();
MutableBlockPos mut = new MutableBlockPos().set(pos);
for (int i = 0; i < max; i++) {
mut.move(dir);
state = world.getBlockState(mut);
if (!state.is(this)) {
return;
}
int size = (i + 2) * 3 / max;
int src = state.getValue(SHAPE).getIndex();
dir = state.getValue(FACING).getOpposite();
if (src < size) {
TripleShape shape = TripleShape.fromIndex(size);
BlocksHelper.setWithoutUpdate(world, mut, state.setValue(SHAPE, shape));
}
}
}
static {
BIG_SHAPES.put(Axis.X, Block.box(0, 2, 2, 16, 14, 14));
BIG_SHAPES.put(Axis.Y, Block.box(2, 0, 2, 14, 16, 14));
BIG_SHAPES.put(Axis.Z, Block.box(2, 2, 0, 14, 14, 16));
MEDIUM_SHAPES.put(Axis.X, Block.box(0, 3, 3, 16, 13, 13));
MEDIUM_SHAPES.put(Axis.Y, Block.box(3, 0, 3, 13, 16, 13));
MEDIUM_SHAPES.put(Axis.Z, Block.box(3, 3, 0, 13, 13, 16));
SMALL_SHAPES.put(Axis.X, Block.box(0, 4, 4, 16, 12, 12));
SMALL_SHAPES.put(Axis.Y, Block.box(4, 0, 4, 12, 16, 12));
SMALL_SHAPES.put(Axis.Z, Block.box(4, 4, 0, 12, 12, 16));
BIG_SHAPES_OPEN.put(Direction.UP, Block.box(2, 0, 2, 14, 14, 14));
BIG_SHAPES_OPEN.put(Direction.DOWN, Block.box(2, 2, 2, 14, 16, 14));
BIG_SHAPES_OPEN.put(Direction.NORTH, Block.box(2, 2, 2, 14, 14, 16));
BIG_SHAPES_OPEN.put(Direction.SOUTH, Block.box(2, 2, 0, 14, 14, 14));
BIG_SHAPES_OPEN.put(Direction.WEST, Block.box(2, 2, 2, 16, 14, 14));
BIG_SHAPES_OPEN.put(Direction.EAST, Block.box(0, 2, 2, 14, 14, 14));
MEDIUM_SHAPES_OPEN.put(Direction.UP, Block.box(3, 0, 3, 13, 13, 13));
MEDIUM_SHAPES_OPEN.put(Direction.DOWN, Block.box(3, 3, 3, 13, 16, 13));
MEDIUM_SHAPES_OPEN.put(Direction.NORTH, Block.box(3, 3, 3, 13, 13, 16));
MEDIUM_SHAPES_OPEN.put(Direction.SOUTH, Block.box(3, 3, 0, 13, 13, 13));
MEDIUM_SHAPES_OPEN.put(Direction.WEST, Block.box(3, 3, 3, 16, 13, 13));
MEDIUM_SHAPES_OPEN.put(Direction.EAST, Block.box(0, 3, 3, 13, 13, 13));
SMALL_SHAPES_OPEN.put(Direction.UP, Block.box(4, 0, 4, 12, 12, 12));
SMALL_SHAPES_OPEN.put(Direction.DOWN, Block.box(4, 4, 4, 12, 16, 12));
SMALL_SHAPES_OPEN.put(Direction.NORTH, Block.box(4, 4, 4, 12, 12, 16));
SMALL_SHAPES_OPEN.put(Direction.SOUTH, Block.box(4, 4, 0, 12, 12, 12));
SMALL_SHAPES_OPEN.put(Direction.WEST, Block.box(4, 4, 4, 16, 12, 12));
SMALL_SHAPES_OPEN.put(Direction.EAST, Block.box(0, 4, 4, 12, 12, 12));
}
}

View file

@ -205,7 +205,10 @@ public class EndBlocks {
public static final Block AMARANITA_FUR = registerBlock("amaranita_fur", new FurBlock(MOSSY_GLOWSHROOM_SAPLING, 15, 4, true));
public static final Block AMARANITA_CAP = registerBlock("amaranita_cap", new AmaranitaCapBlock());
public static final Block NEON_CACTUS = registerBlock("neon_cactus", new NeonCactusBlock());
public static final Block NEON_CACTUS = registerBlock("neon_cactus", new NeonCactusPlantBlock());
public static final Block NEON_CACTUS_BLOCK = registerBlock("neon_cactus_block", new NeonCactusBlock());
public static final Block NEON_CACTUS_BLOCK_STAIRS = registerBlock("neon_cactus_stairs", new EndStairsBlock(NEON_CACTUS_BLOCK));
public static final Block NEON_CACTUS_BLOCK_SLAB = registerBlock("neon_cactus_slab", new EndSlabBlock(NEON_CACTUS_BLOCK));
// Crops
public static final Block SHADOW_BERRY = registerBlock("shadow_berry", new ShadowBerryBlock());

View file

@ -3,95 +3,24 @@ package ru.betterend.world.features;
import java.util.Random;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import ru.betterend.blocks.BlockProperties;
import ru.betterend.blocks.BlockProperties.CactusBottom;
import ru.betterend.blocks.BlockProperties.TripleShape;
import ru.betterend.blocks.NeonCactusPlantBlock;
import ru.betterend.registry.EndBlocks;
import ru.betterend.util.BlocksHelper;
import ru.betterend.util.MHelper;
public class NeonCactusFeature extends DefaultFeature {
@Override
public boolean place(WorldGenLevel world, ChunkGenerator chunkGenerator, Random random, BlockPos pos,
NoneFeatureConfiguration config) {
if (!world.getBlockState(pos.below()).is(EndBlocks.ENDSTONE_DUST)) {
public boolean place(WorldGenLevel world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, NoneFeatureConfiguration config) {
BlockState ground = world.getBlockState(pos.below());
if (!ground.is(EndBlocks.ENDSTONE_DUST) && !ground.is(EndBlocks.END_MOSS)) {
return false;
}
int h = MHelper.randRange(5, 20, random);
MutableBlockPos mut = new MutableBlockPos().set(pos);
Direction hor = BlocksHelper.randomHorizontal(random);
for (int i = 0; i < h; i++) {
if (!world.getBlockState(mut).getMaterial().isReplaceable()) {
break;
}
int size = (h - i) >> 2;
BlockState state = EndBlocks.NEON_CACTUS.defaultBlockState().setValue(BlockProperties.TRIPLE_SHAPE, getBySize(size)).setValue(BlockStateProperties.FACING, Direction.UP);
if (i == 0) {
BlockState down = world.getBlockState(mut.below());
if (down.is(Blocks.END_STONE) || down.is(EndBlocks.ENDSTONE_DUST)) {
state = state.setValue(BlockProperties.CACTUS_BOTTOM, CactusBottom.SAND);
}
else if (down.is(EndBlocks.END_MOSS)) {
state = state.setValue(BlockProperties.CACTUS_BOTTOM, CactusBottom.MOSS);
}
else {
state = state.setValue(BlockProperties.CACTUS_BOTTOM, CactusBottom.EMPTY);
}
}
else {
state = state.setValue(BlockProperties.CACTUS_BOTTOM, CactusBottom.EMPTY);
}
BlocksHelper.setWithUpdate(world, mut, state);
if (i > 2 && i < (h - 1) && random.nextBoolean()) {
int length = h - i - MHelper.randRange(1, 2, random);
if (length > 0) {
Direction dir2 = hor;
hor = hor.getClockWise();
int bsize = i > ((h << 1) / 3) ? 0 : size > 1 ? 1 : size;
branch(world, mut.relative(dir2), dir2, random, length, bsize);
}
}
mut.move(Direction.UP);
}
NeonCactusPlantBlock cactus = ((NeonCactusPlantBlock) EndBlocks.NEON_CACTUS);
cactus.growPlant(world, pos, random);
return true;
}
private void branch(WorldGenLevel world, BlockPos pos, Direction dir, Random random, int length, int size) {
int rotIndex = length >> 2;
MutableBlockPos mut = new MutableBlockPos().set(pos);
Direction hor = BlocksHelper.randomHorizontal(random);
for (int i = 0; i < length; i++) {
if (!world.getBlockState(mut).getMaterial().isReplaceable()) {
return;
}
BlocksHelper.setWithUpdate(world, mut,
EndBlocks.NEON_CACTUS.defaultBlockState().setValue(BlockProperties.TRIPLE_SHAPE, getBySize(size))
.setValue(BlockStateProperties.FACING, dir));
if (i == rotIndex) {
dir = Direction.UP;
size--;
}
if (i > 1 && i < (length - 1) && random.nextBoolean()) {
Direction dir2 = dir == Direction.UP ? hor : Direction.UP;
hor = hor.getClockWise();
branch(world, mut.relative(dir2), dir2, random, MHelper.randRange(length / 4, length / 2, random),
size > 0 ? size - 1 : size);
}
mut.move(dir);
}
}
private TripleShape getBySize(int size) {
return size < 1 ? TripleShape.TOP : size == 1 ? TripleShape.MIDDLE : TripleShape.BOTTOM;
}
}

View file

@ -0,0 +1,15 @@
{
"variants": {
"type=bottom": {
"model": "betterend:block/neon_cactus_slab"
},
"type=top": {
"model": "betterend:block/neon_cactus_slab",
"x": 180,
"uvlock": true
},
"type=double": {
"model": "betterend:block/neon_cactus_block"
}
}
}

View file

@ -0,0 +1,44 @@
{
"variants": {
"facing=east,half=bottom,shape=straight": { "model": "betterend:block/neon_cactus_stairs" },
"facing=west,half=bottom,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "y": 180 },
"facing=south,half=bottom,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "y": 90 },
"facing=north,half=bottom,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "y": 270 },
"facing=east,half=bottom,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer" },
"facing=west,half=bottom,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "y": 180 },
"facing=south,half=bottom,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "y": 90 },
"facing=north,half=bottom,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "y": 270 },
"facing=east,half=bottom,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "y": 270 },
"facing=west,half=bottom,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "y": 90 },
"facing=south,half=bottom,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer" },
"facing=north,half=bottom,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "y": 180 },
"facing=east,half=bottom,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner" },
"facing=west,half=bottom,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "y": 180 },
"facing=south,half=bottom,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "y": 90 },
"facing=north,half=bottom,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "y": 270 },
"facing=east,half=bottom,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "y": 270 },
"facing=west,half=bottom,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "y": 90 },
"facing=south,half=bottom,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner" },
"facing=north,half=bottom,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "y": 180 },
"facing=east,half=top,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "x": 180 },
"facing=west,half=top,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "x": 180, "y": 180 },
"facing=south,half=top,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "x": 180, "y": 90 },
"facing=north,half=top,shape=straight": { "model": "betterend:block/neon_cactus_stairs", "x": 180, "y": 270 },
"facing=east,half=top,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180, "y": 90 },
"facing=west,half=top,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180, "y": 270 },
"facing=south,half=top,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180, "y": 180 },
"facing=north,half=top,shape=outer_right": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180 },
"facing=east,half=top,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180 },
"facing=west,half=top,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180, "y": 180 },
"facing=south,half=top,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180, "y": 90 },
"facing=north,half=top,shape=outer_left": { "model": "betterend:block/neon_cactus_stairs_outer", "x": 180, "y": 270 },
"facing=east,half=top,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180, "y": 90 },
"facing=west,half=top,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180, "y": 270 },
"facing=south,half=top,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180, "y": 180 },
"facing=north,half=top,shape=inner_right": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180 },
"facing=east,half=top,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180 },
"facing=west,half=top,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180, "y": 180 },
"facing=south,half=top,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180, "y": 90 },
"facing=north,half=top,shape=inner_left": { "model": "betterend:block/neon_cactus_stairs_inner", "x": 180, "y": 270 }
}
}

View file

@ -0,0 +1,8 @@
{
"parent": "block/slab",
"textures": {
"bottom": "betterend:block/neon_cactus_block_top",
"top": "betterend:block/neon_cactus_block_top",
"side": "betterend:block/neon_cactus_block_side"
}
}

View file

@ -0,0 +1,8 @@
{
"parent": "minecraft:block/stairs",
"textures": {
"bottom": "betterend:block/neon_cactus_block_top",
"top": "betterend:block/neon_cactus_block_top",
"side": "betterend:block/neon_cactus_block_side"
}
}

View file

@ -0,0 +1,8 @@
{
"parent": "minecraft:block/inner_stairs",
"textures": {
"bottom": "betterend:block/neon_cactus_block_top",
"top": "betterend:block/neon_cactus_block_top",
"side": "betterend:block/neon_cactus_block_side"
}
}

View file

@ -0,0 +1,8 @@
{
"parent": "minecraft:block/outer_stairs",
"textures": {
"bottom": "betterend:block/neon_cactus_block_top",
"top": "betterend:block/neon_cactus_block_top",
"side": "betterend:block/neon_cactus_block_side"
}
}

View file

@ -0,0 +1,3 @@
{
"parent": "betterend:block/neon_cactus_slab"
}

View file

@ -0,0 +1,3 @@
{
"parent": "betterend:block/neon_cactus_stairs"
}