VERY TALL AND SHORT AND BIG

also made rainbow ant rare in elythia
sniffer died
fyling squireal anim overhual
This commit is contained in:
craisinlord committed 2026-07-07 01:22:10 -05:00
1 parent 3e25149618
commit 60405560c2
30 files changed
+909

No files matched your search

@@ -0,0 +1,255 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.Antarchy;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
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.BonemealableBlock;
import net.minecraft.world.level.block.BushBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
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.DirectionProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Multiblock bush grown with bonemeal. The origin block (this one) carries the
* loot/age; the rest of the footprint is filled with invisible
* {@link BigBushPartBlock}s.
*
* age 0: single block (1x1x1)
* age 1: 2x2x2 footprint; FACING + FACING.getClockWise() pick the horizontal quadrant
* age 2: 3x3x3 footprint centered on the origin column
*
* Grown stages require a sturdy support block under every column of their
* footprint. Growing from 2x2 to 3x3 may relocate the origin to any of the four
* occupied columns to find room.
*/
public class BigBushBlock extends BushBlock implements BonemealableBlock {
public static final MapCodec<BigBushBlock> CODEC = Block.simpleCodec(BigBushBlock::new);
public static final IntegerProperty AGE = BlockStateProperties.AGE_2;
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
private static final VoxelShape SMALL_SHAPE = Block.box(2.0, 0.0, 2.0, 14.0, 13.0, 14.0);
public BigBushBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any()
.setValue(AGE, 0)
.setValue(FACING, Direction.EAST));
}
@Override
public MapCodec<BigBushBlock> codec() {
return CODEC;
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(AGE, FACING);
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(AGE) == 0 ? SMALL_SHAPE : Shapes.block();
}
/** All part positions belonging to a bush with the given origin state/pos (origin itself excluded). */
public static List<BlockPos> partPositions(BlockState state, BlockPos origin) {
List<BlockPos> positions = new ArrayList<>();
int age = state.getValue(AGE);
if (age == 1) {
Direction forward = state.getValue(FACING);
Direction side = forward.getClockWise();
for (int dy = 0; dy <= 1; dy++) {
for (BlockPos column : new BlockPos[]{
origin, origin.relative(forward), origin.relative(side), origin.relative(forward).relative(side)}) {
BlockPos pos = column.above(dy);
if (!pos.equals(origin)) {
positions.add(pos);
}
}
}
} else if (age == 2) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = 0; dy <= 2; dy++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dy == 0 && dz == 0) {
continue;
}
positions.add(origin.offset(dx, dy, dz));
}
}
}
}
return positions;
}
/** Ground-level columns of the footprint; each needs a sturdy block below it. */
public static List<BlockPos> supportColumns(BlockState state, BlockPos origin) {
List<BlockPos> columns = new ArrayList<>();
int age = state.getValue(AGE);
if (age == 1) {
Direction forward = state.getValue(FACING);
Direction side = forward.getClockWise();
columns.add(origin);
columns.add(origin.relative(forward));
columns.add(origin.relative(side));
columns.add(origin.relative(forward).relative(side));
} else if (age == 2) {
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
columns.add(origin.offset(dx, 0, dz));
}
}
} else {
columns.add(origin);
}
return columns;
}
public static boolean hasFullSupport(BlockState state, LevelReader level, BlockPos origin) {
for (BlockPos column : supportColumns(state, origin)) {
BlockPos below = column.below();
if (!level.getBlockState(below).isFaceSturdy(level, below, Direction.UP)) {
return false;
}
}
return true;
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
if (state.getValue(AGE) == 0) {
return super.canSurvive(state, level, pos);
}
return hasFullSupport(state, level, pos);
}
private static Block partBlock() {
return BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "big_bush_part"));
}
private record GrowthTarget(BlockPos origin, BlockState state) {}
@Nullable
private GrowthTarget findGrowthTarget(LevelReader level, BlockPos pos, BlockState state, RandomSource random) {
int age = state.getValue(AGE);
if (age == 0) {
List<Direction> facings = new ArrayList<>(Direction.Plane.HORIZONTAL.stream().toList());
for (int i = facings.size() - 1; i > 0; i--) {
Direction swap = facings.set(random.nextInt(i + 1), facings.get(i));
facings.set(i, swap);
}
for (Direction facing : facings) {
BlockState grown = state.setValue(AGE, 1).setValue(FACING, facing);
if (canGrowTo(level, pos, grown, Set.of(pos))) {
return new GrowthTarget(pos, grown);
}
}
return null;
}
if (age == 1) {
Direction forward = state.getValue(FACING);
Direction side = forward.getClockWise();
Set<BlockPos> owned = new HashSet<>(partPositions(state, pos));
owned.add(pos);
BlockState grown = state.setValue(AGE, 2);
// Any of the four occupied columns can become the new center of the 3x3.
for (BlockPos candidate : new BlockPos[]{
pos, pos.relative(forward).relative(side), pos.relative(forward), pos.relative(side)}) {
if (canGrowTo(level, candidate, grown, owned)) {
return new GrowthTarget(candidate, grown);
}
}
}
return null;
}
private boolean canGrowTo(LevelReader level, BlockPos newOrigin, BlockState grownState, Set<BlockPos> owned) {
if (!hasFullSupport(grownState, level, newOrigin)) {
return false;
}
List<BlockPos> positions = new ArrayList<>(partPositions(grownState, newOrigin));
positions.add(newOrigin);
for (BlockPos pos : positions) {
if (owned.contains(pos)) {
continue;
}
if (!level.getBlockState(pos).canBeReplaced()) {
return false;
}
}
return true;
}
public static void placeBushBlocks(LevelAccessor level, BlockPos origin, BlockState grownState, int flags) {
level.setBlock(origin, grownState, flags);
Block part = partBlock();
BlockPos capPos = grownState.getValue(AGE) == 2 ? origin.above(2) : null;
for (BlockPos pos : partPositions(grownState, origin)) {
level.setBlock(pos, part.defaultBlockState()
.setValue(BigBushPartBlock.CAP, pos.equals(capPos)), flags);
}
}
private void placeStage(ServerLevel level, BlockPos origin, BlockState grownState) {
placeBushBlocks(level, origin, grownState, 3);
}
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock())) {
for (BlockPos partPos : partPositions(state, pos)) {
if (level.getBlockState(partPos).getBlock() instanceof BigBushPartBlock) {
level.setBlock(partPos, Blocks.AIR.defaultBlockState(), 35);
}
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
return state.getValue(AGE) < 2;
}
@Override
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
return findGrowthTarget(level, pos, state, random) != null;
}
@Override
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
GrowthTarget target = findGrowthTarget(level, pos, state, random);
if (target == null) {
return;
}
if (!target.origin().equals(pos)) {
// Relocating the origin: removing it cascades and clears the old parts.
level.removeBlock(pos, false);
}
placeStage(level, target.origin(), target.state());
}
}
@@ -0,0 +1,132 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
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.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
/**
* Invisible filler block for the grown {@link BigBushBlock} footprint. Breaking a
* part destroys the whole bush (drops come from the origin's loot table). The one
* part directly two above the origin of a stage-3 bush renders the top slice of
* the oversized cross model (CAP=true).
*/
public class BigBushPartBlock extends Block {
public static final MapCodec<BigBushPartBlock> CODEC = Block.simpleCodec(BigBushPartBlock::new);
public static final BooleanProperty CAP = BooleanProperty.create("cap");
public BigBushPartBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(CAP, false));
}
@Override
public MapCodec<BigBushPartBlock> codec() {
return CODEC;
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(CAP);
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return Shapes.block();
}
/** Finds the origin bush this part belongs to, or null if orphaned. */
@Nullable
public static BlockPos findOrigin(LevelReader level, BlockPos partPos) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -2; dy <= 0; dy++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dy == 0 && dz == 0) {
continue;
}
BlockPos candidate = partPos.offset(dx, dy, dz);
BlockState state = level.getBlockState(candidate);
if (state.getBlock() instanceof BigBushBlock
&& BigBushBlock.partPositions(state, candidate).contains(partPos)) {
return candidate;
}
}
}
}
return null;
}
@Override
public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
if (!level.isClientSide) {
BlockPos origin = findOrigin(level, pos);
if (origin != null) {
BlockState originState = level.getBlockState(origin);
if (!player.getAbilities().instabuild) {
Block.dropResources(originState, level, origin, null, player, player.getMainHandItem());
}
level.destroyBlock(origin, false);
}
}
return super.playerWillDestroy(level, pos, state, player);
}
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock()) && !level.isClientSide) {
BlockPos origin = findOrigin(level, pos);
if (origin != null) {
level.destroyBlock(origin, true);
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState,
LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
level.scheduleTick(pos, this, 1);
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
}
@Override
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
BlockPos origin = findOrigin(level, pos);
if (origin == null) {
level.removeBlock(pos, false);
return;
}
// Grown bushes need sturdy support under the whole footprint; break with
// drops if any of it is gone (mirrors plants popping when their dirt breaks).
BlockState originState = level.getBlockState(origin);
if (!originState.canSurvive(level, origin)) {
level.destroyBlock(origin, true);
}
}
@Override
public ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state) {
BlockPos origin = findOrigin(level, pos);
if (origin != null) {
return new ItemStack(level.getBlockState(origin).getBlock());
}
return ItemStack.EMPTY;
}
}
@@ -0,0 +1,52 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
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.BonemealableBlock;
import net.minecraft.world.level.block.BushBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class VeryShortGrassBlock extends BushBlock implements BonemealableBlock {
public static final MapCodec<VeryShortGrassBlock> CODEC = Block.simpleCodec(VeryShortGrassBlock::new);
private static final VoxelShape SHAPE = Block.box(2.0, 0.0, 2.0, 14.0, 8.0, 14.0);
public VeryShortGrassBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
public MapCodec<VeryShortGrassBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
return true;
}
@Override
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
return true;
}
@Override
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
level.setBlock(pos, Blocks.SHORT_GRASS.defaultBlockState(), 3);
}
}
@@ -0,0 +1,56 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.block.BigBushBlock;
import com.craisinlord.antarchy.content.block.BigBushPartBlock;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import java.util.Objects;
public final class BigBushCameraHider {
private static BlockPos hiddenOrigin;
private BigBushCameraHider() {
}
public static void update(Minecraft mc) {
BlockPos newOrigin = null;
if (mc.level != null && mc.player != null) {
Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition();
BlockPos pos = BlockPos.containing(cameraPos);
BlockState state = mc.level.getBlockState(pos);
if (state.getBlock() instanceof BigBushBlock && state.getValue(BigBushBlock.AGE) > 0) {
newOrigin = pos;
} else if (state.getBlock() instanceof BigBushPartBlock) {
newOrigin = BigBushPartBlock.findOrigin(mc.level, pos);
}
}
if (!Objects.equals(newOrigin, hiddenOrigin)) {
BlockPos previous = hiddenOrigin;
hiddenOrigin = newOrigin;
markDirty(mc, previous);
markDirty(mc, newOrigin);
}
}
private static void markDirty(Minecraft mc, BlockPos origin) {
if (origin != null && mc.levelRenderer != null) {
mc.levelRenderer.setBlocksDirty(
origin.getX() - 2, origin.getY() - 1, origin.getZ() - 2,
origin.getX() + 2, origin.getY() + 3, origin.getZ() + 2);
}
}
public static boolean shouldFade(BlockPos pos) {
BlockPos origin = hiddenOrigin;
if (origin == null) {
return false;
}
return Math.abs(pos.getX() - origin.getX()) <= 1
&& Math.abs(pos.getZ() - origin.getZ()) <= 1
&& pos.getY() >= origin.getY()
&& pos.getY() <= origin.getY() + 2;
}
}
@@ -0,0 +1,26 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.block.BigBushBlock;
import com.craisinlord.antarchy.content.block.BigBushPartBlock;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
public final class BigBushRenderHelper {
private BigBushRenderHelper() {
}
public static boolean isInsideBigBush(LivingEntity entity) {
Level level = entity.level();
return isBush(level, entity.blockPosition()) || isBush(level, BlockPos.containing(entity.getEyePosition()));
}
private static boolean isBush(Level level, BlockPos pos) {
BlockState state = level.getBlockState(pos);
if (state.getBlock() instanceof BigBushPartBlock) {
return true;
}
return state.getBlock() instanceof BigBushBlock && state.getValue(BigBushBlock.AGE) > 0;
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.content.item;
import java.util.List;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.block.Block;
public class BigBushBlockItem extends BlockItem {
public BigBushBlockItem(Block block, Properties properties) {
super(block, properties);
}
@Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(Component.translatable("item.antarchy.big_bush.tooltip").withStyle(ChatFormatting.GRAY));
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
}
@@ -0,0 +1,25 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.block.BigBushBlock;
import com.craisinlord.antarchy.content.block.BigBushPartBlock;
import com.craisinlord.antarchy.content.client.AlphaVertexConsumerProxy;
import com.craisinlord.antarchy.content.client.BigBushCameraHider;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(BlockRenderDispatcher.class)
public abstract class BlockRenderDispatcherBigBushMixin {
@ModifyVariable(method = "renderBatched", at = @At("HEAD"), argsOnly = true)
private VertexConsumer antarchy$fadeBigBushAroundCamera(VertexConsumer consumer, BlockState state, BlockPos pos) {
if ((state.getBlock() instanceof BigBushBlock || state.getBlock() instanceof BigBushPartBlock)
&& BigBushCameraHider.shouldFade(pos)) {
return AlphaVertexConsumerProxy.wrap(consumer, 0.35F);
}
return consumer;
}
}
@@ -0,0 +1,54 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.BigBushRenderHelper;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererBigBushMixin {
@Unique
private boolean antarchy$inBigBush;
@Inject(
method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At("HEAD")
)
private void antarchy$checkBigBush(LivingEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
this.antarchy$inBigBush = BigBushRenderHelper.isInsideBigBush(entity);
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Inject(method = "getRenderType", at = @At("HEAD"), cancellable = true)
private void antarchy$bigBushRenderType(LivingEntity entity, boolean bodyVisible, boolean translucent, boolean glowing, CallbackInfoReturnable<RenderType> cir) {
if (bodyVisible && !translucent && !glowing && BigBushRenderHelper.isInsideBigBush(entity)) {
cir.setReturnValue(RenderType.itemEntityTranslucentCull(((EntityRenderer) (Object) this).getTextureLocation(entity)));
}
}
@Inject(method = "shouldShowName(Lnet/minecraft/world/entity/LivingEntity;)Z", at = @At("HEAD"), cancellable = true)
private void antarchy$hideBigBushNameTag(LivingEntity entity, CallbackInfoReturnable<Boolean> cir) {
if (BigBushRenderHelper.isInsideBigBush(entity)) {
cir.setReturnValue(false);
}
}
@ModifyArg(
method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/EntityModel;renderToBuffer(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;III)V"),
index = 4
)
private int antarchy$bigBushAlpha(int color) {
return this.antarchy$inBigBush && color == -1 ? 0x99FFFFFF : color;
}
}