nyxium, typhonite pillar, nadir ferns, duskbell, gorevine, royal assailant staff, manticore wings, manticore wing

This commit is contained in:
craisinlord committed 2026-09-01 21:54:41 -05:00
1 parent 069d90d6e2
commit e173ae66f5
147 files changed
+2544 -290

No files matched your search

@@ -15,8 +15,7 @@ import net.minecraft.world.level.block.state.BlockState;
public class AntigravelBlock extends Block {
public static final MapCodec<AntigravelBlock> CODEC = simpleCodec(AntigravelBlock::new);
private static final int SETTLE_DELAY = 6;
private static final int SETTLE_ATTEMPT_DENOMINATOR = 3;
private static final int SETTLE_DELAY = 2;
public AntigravelBlock(BlockBehaviour.Properties properties) {
super(properties);
@@ -52,10 +51,6 @@ public class AntigravelBlock extends Block {
if (!passable) {
return;
}
if (random.nextInt(SETTLE_ATTEMPT_DENOMINATOR) != 0) {
level.scheduleTick(pos, this, SETTLE_DELAY + random.nextInt(SETTLE_DELAY));
return;
}
UpwardFallingBlockEntity.fallUp(level, pos, state, false);
}
}
@@ -0,0 +1,130 @@
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.item.context.BlockPlaceContext;
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.BonemealableBlock;
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;
public class DirectionalNyxiumBlock extends Block implements BonemealableBlock {
public static final MapCodec<DirectionalNyxiumBlock> CODEC = simpleCodec(properties -> new DirectionalNyxiumBlock(properties, Type.NADIR));
public static final DirectionProperty VERTICAL_DIRECTION = BlockStateProperties.VERTICAL_DIRECTION;
private static final ResourceLocation NYXITE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "nyxite");
private static final ResourceLocation NADIR_FERN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "nadir_fern");
private static final ResourceLocation DUSKBELL_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "duskbell");
private static final ResourceLocation NADIR_SAPLING_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "nadir_sapling");
private static final ResourceLocation WHIRLFLOWER_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "whirlflower");
private static final ResourceLocation SPIRALING_VINES_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "spiraling_vines");
private final Type type;
public DirectionalNyxiumBlock(BlockBehaviour.Properties properties, Type type) {
super(properties);
this.type = type;
this.registerDefaultState(this.stateDefinition.any().setValue(VERTICAL_DIRECTION, Direction.UP));
}
@Override
protected MapCodec<? extends Block> codec() {
return CODEC;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(
VERTICAL_DIRECTION,
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(VERTICAL_DIRECTION);
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
return hasAdjacentNyxite(level, pos) || level.getBlockState(pos.relative(state.getValue(VERTICAL_DIRECTION))).isAir();
}
@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) {
spreadToNearbyNyxite(level, random, pos, state);
BlockPos growthPos = pos.relative(state.getValue(VERTICAL_DIRECTION));
if (level.getBlockState(growthPos).isAir()) {
placeVegetation(level, random, growthPos, state.getValue(VERTICAL_DIRECTION));
}
}
private void spreadToNearbyNyxite(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
for (int i = 0; i < 24; i++) {
BlockPos targetPos = pos.offset(
random.nextInt(5) - 2,
random.nextInt(3) - 1,
random.nextInt(5) - 2
);
if (isNyxite(level.getBlockState(targetPos))) {
level.setBlock(targetPos, state, Block.UPDATE_ALL);
}
}
}
private void placeVegetation(ServerLevel level, RandomSource random, BlockPos pos, Direction direction) {
ResourceLocation id = switch (this.type) {
case NADIR -> {
if (direction == Direction.DOWN && random.nextInt(7) == 0) {
yield NADIR_SAPLING_ID;
}
yield random.nextInt(4) == 0 ? DUSKBELL_ID : NADIR_FERN_ID;
}
case VERDANT -> random.nextInt(3) == 0 ? SPIRALING_VINES_ID : WHIRLFLOWER_ID;
};
Block block = BuiltInRegistries.BLOCK.get(id);
BlockState growthState = block.defaultBlockState();
if (growthState.hasProperty(SimpleDirectionalVineBlock.GROWTH_DIRECTION)) {
growthState = growthState.setValue(SimpleDirectionalVineBlock.GROWTH_DIRECTION, direction);
}
if (growthState.hasProperty(VERTICAL_DIRECTION)) {
growthState = growthState.setValue(VERTICAL_DIRECTION, direction);
}
if (growthState.canSurvive(level, pos)) {
level.setBlock(pos, growthState, Block.UPDATE_ALL);
}
}
private static boolean hasAdjacentNyxite(LevelReader level, BlockPos pos) {
for (Direction direction : Direction.values()) {
if (isNyxite(level.getBlockState(pos.relative(direction)))) {
return true;
}
}
return false;
}
private static boolean isNyxite(BlockState state) {
return BuiltInRegistries.BLOCK.getKey(state.getBlock()).equals(NYXITE_ID);
}
public enum Type {
NADIR,
VERDANT
}
}
@@ -0,0 +1,81 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
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.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.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class DirectionalThoraxisFlowerBlock extends BushBlock {
public static final MapCodec<DirectionalThoraxisFlowerBlock> CODEC = simpleCodec(DirectionalThoraxisFlowerBlock::new);
public static final DirectionProperty VERTICAL_DIRECTION = BlockStateProperties.VERTICAL_DIRECTION;
private static final VoxelShape UP_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 13.0D, 13.0D);
private static final VoxelShape DOWN_SHAPE = Block.box(3.0D, 3.0D, 3.0D, 13.0D, 16.0D, 13.0D);
public DirectionalThoraxisFlowerBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(VERTICAL_DIRECTION, Direction.UP));
}
@Override
protected MapCodec<? extends BushBlock> codec() {
return CODEC;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(
VERTICAL_DIRECTION,
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
);
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(VERTICAL_DIRECTION) == Direction.DOWN ? DOWN_SHAPE : UP_SHAPE;
}
@Override
protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
return true;
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
Direction growthDirection = state.getValue(VERTICAL_DIRECTION);
BlockPos supportPos = pos.relative(growthDirection.getOpposite());
BlockState supportState = level.getBlockState(supportPos);
return supportState.isFaceSturdy(level, supportPos, growthDirection);
}
@Override
protected BlockState updateShape(
BlockState state,
Direction direction,
BlockState neighborState,
LevelAccessor level,
BlockPos pos,
BlockPos neighborPos
) {
return state.canSurvive(level, pos)
? super.updateShape(state, direction, neighborState, level, pos, neighborPos)
: Blocks.AIR.defaultBlockState();
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(VERTICAL_DIRECTION);
}
}
@@ -0,0 +1,39 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
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.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
public class DreamSandstoneBlock extends Block {
public static final MapCodec<DreamSandstoneBlock> CODEC = Block.simpleCodec(DreamSandstoneBlock::new);
public static final DirectionProperty VERTICAL_DIRECTION = BlockStateProperties.VERTICAL_DIRECTION;
public DreamSandstoneBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(VERTICAL_DIRECTION, Direction.UP));
}
@Override
public MapCodec<DreamSandstoneBlock> codec() {
return CODEC;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(
VERTICAL_DIRECTION,
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(VERTICAL_DIRECTION);
}
}
@@ -0,0 +1,24 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.world.level.block.BushBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
public class GorevineBlock extends SimpleDirectionalVineBlock {
public static final MapCodec<GorevineBlock> CODEC = simpleCodec(GorevineBlock::new);
private static final int MAX_GROWTH_DISTANCE = 12;
public GorevineBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
protected int maxDistance() {
return MAX_GROWTH_DISTANCE;
}
@Override
public MapCodec<? extends BushBlock> codec() {
return CODEC;
}
}
@@ -0,0 +1,138 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
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.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.EnumProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class LargeNadirFernBlock extends Block {
public static final MapCodec<LargeNadirFernBlock> CODEC = simpleCodec(LargeNadirFernBlock::new);
public static final DirectionProperty VERTICAL_DIRECTION = BlockStateProperties.VERTICAL_DIRECTION;
public static final EnumProperty<Half> HALF = EnumProperty.create("half", Half.class);
private static final VoxelShape SHAPE = Block.box(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D);
public LargeNadirFernBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any()
.setValue(VERTICAL_DIRECTION, Direction.UP)
.setValue(HALF, Half.BOTTOM));
}
@Override
protected MapCodec<? extends Block> codec() {
return CODEC;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
Direction direction = context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP;
BlockPos otherPos = context.getClickedPos().relative(direction);
if (!context.getLevel().getBlockState(otherPos).canBeReplaced(context)) {
return null;
}
return this.defaultBlockState().setValue(VERTICAL_DIRECTION, direction);
}
@Override
public void setPlacedBy(net.minecraft.world.level.Level level, BlockPos pos, BlockState state, net.minecraft.world.entity.LivingEntity placer, net.minecraft.world.item.ItemStack stack) {
placeOtherHalf(level, pos, state, Block.UPDATE_ALL);
}
public static void placeAt(LevelAccessor level, BlockPos pos, BlockState bottomState, int flags) {
level.setBlock(pos, bottomState.setValue(HALF, Half.BOTTOM), flags);
placeOtherHalf(level, pos, bottomState, flags);
}
private static void placeOtherHalf(LevelAccessor level, BlockPos pos, BlockState state, int flags) {
Direction direction = state.getValue(VERTICAL_DIRECTION);
level.setBlock(pos.relative(direction), state.setValue(HALF, Half.TOP), flags);
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
Direction direction = state.getValue(VERTICAL_DIRECTION);
if (state.getValue(HALF) == Half.TOP) {
BlockState otherState = level.getBlockState(pos.relative(direction.getOpposite()));
return otherState.is(this)
&& otherState.getValue(HALF) == Half.BOTTOM
&& otherState.getValue(VERTICAL_DIRECTION) == direction;
}
BlockPos supportPos = pos.relative(direction.getOpposite());
BlockState supportState = level.getBlockState(supportPos);
BlockState topState = level.getBlockState(pos.relative(direction));
boolean topMatches = topState.isAir()
|| topState.is(this)
&& topState.getValue(HALF) == Half.TOP
&& topState.getValue(VERTICAL_DIRECTION) == direction;
return topMatches && supportState.isFaceSturdy(level, supportPos, direction);
}
@Override
protected BlockState updateShape(
BlockState state,
Direction direction,
BlockState neighborState,
LevelAccessor level,
BlockPos pos,
BlockPos neighborPos
) {
return state.canSurvive(level, pos)
? super.updateShape(state, direction, neighborState, level, pos, neighborPos)
: Blocks.AIR.defaultBlockState();
}
@Override
protected void onRemove(BlockState state, net.minecraft.world.level.Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock())) {
Direction verticalDirection = state.getValue(VERTICAL_DIRECTION);
BlockPos otherPos = state.getValue(HALF) == Half.BOTTOM
? pos.relative(verticalDirection)
: pos.relative(verticalDirection.getOpposite());
if (level.getBlockState(otherPos).is(this)) {
level.setBlock(otherPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(VERTICAL_DIRECTION, HALF);
}
public enum Half implements StringRepresentable {
TOP("top"),
BOTTOM("bottom");
private final String name;
Half(String name) {
this.name = name;
}
@Override
public String getSerializedName() {
return this.name;
}
}
}
@@ -0,0 +1,50 @@
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.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
public class NadirFernBlock extends DirectionalThoraxisFlowerBlock implements BonemealableBlock {
public static final MapCodec<NadirFernBlock> CODEC = simpleCodec(NadirFernBlock::new);
private static final ResourceLocation LARGE_NADIR_FERN_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "large_nadir_fern");
public NadirFernBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
protected MapCodec<? extends net.minecraft.world.level.block.BushBlock> codec() {
return CODEC;
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
return level.getBlockState(pos.relative(state.getValue(VERTICAL_DIRECTION))).isAir();
}
@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) {
Direction direction = state.getValue(VERTICAL_DIRECTION);
Block largeFern = BuiltInRegistries.BLOCK.get(LARGE_NADIR_FERN_ID);
BlockState lowerState = largeFern.defaultBlockState()
.setValue(LargeNadirFernBlock.VERTICAL_DIRECTION, direction)
.setValue(LargeNadirFernBlock.HALF, LargeNadirFernBlock.Half.BOTTOM);
LargeNadirFernBlock.placeAt(level, pos, lowerState, Block.UPDATE_ALL);
}
}
@@ -0,0 +1,179 @@
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.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.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.BooleanProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class SimpleDirectionalVineBlock extends BushBlock implements BonemealableBlock {
public static final MapCodec<SimpleDirectionalVineBlock> CODEC = simpleCodec(SimpleDirectionalVineBlock::new);
public static final EnumProperty<Direction> GROWTH_DIRECTION = EnumProperty.create("growth_direction", Direction.class, Direction.UP, Direction.DOWN);
public static final BooleanProperty TOP_CAP = BooleanProperty.create("top_cap");
public static final BooleanProperty BOTTOM_CAP = BooleanProperty.create("bottom_cap");
public static final IntegerProperty DISTANCE = IntegerProperty.create("distance", 0, 6);
protected static final int MAX_DISTANCE = 6;
protected static final VoxelShape SHAPE = Block.box(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D);
public SimpleDirectionalVineBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any()
.setValue(GROWTH_DIRECTION, Direction.DOWN)
.setValue(TOP_CAP, false)
.setValue(BOTTOM_CAP, true)
.setValue(DISTANCE, 0));
}
@Override
public MapCodec<? extends BushBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockGetter level = context.getLevel();
BlockPos pos = context.getClickedPos();
Direction growthDirection = resolveGrowthDirection(level, pos);
return computeState(this.defaultBlockState().setValue(GROWTH_DIRECTION, growthDirection).setValue(DISTANCE, 0), level, pos);
}
private Direction resolveGrowthDirection(BlockGetter level, BlockPos pos) {
BlockState belowState = level.getBlockState(pos.below());
if (belowState.is(this) && belowState.getValue(GROWTH_DIRECTION) == Direction.UP) {
return Direction.UP;
}
BlockState aboveState = level.getBlockState(pos.above());
if (aboveState.is(this) && aboveState.getValue(GROWTH_DIRECTION) == Direction.DOWN) {
return Direction.DOWN;
}
return hasSturdyAnchor(level, pos, Direction.UP) ? Direction.DOWN : Direction.UP;
}
private static boolean hasSturdyAnchor(BlockGetter level, BlockPos pos, Direction towardAnchor) {
BlockPos anchorPos = pos.relative(towardAnchor);
BlockState anchorState = level.getBlockState(anchorPos);
return anchorState.isFaceSturdy(level, anchorPos, towardAnchor.getOpposite());
}
@Override
protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
return state.is(this) || state.isFaceSturdy(level, pos, Direction.DOWN) || state.isFaceSturdy(level, pos, Direction.UP);
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
Direction anchorDirection = state.getValue(GROWTH_DIRECTION).getOpposite();
BlockPos anchorPos = pos.relative(anchorDirection);
BlockState anchorState = level.getBlockState(anchorPos);
if (anchorState.is(this)) {
return true;
}
return anchorState.isFaceSturdy(level, anchorPos, anchorDirection.getOpposite());
}
@Override
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
if (!state.canSurvive(level, pos)) {
return net.minecraft.world.level.block.Blocks.AIR.defaultBlockState();
}
if (direction == Direction.UP || direction == Direction.DOWN) {
return computeState(state, level, pos);
}
return state;
}
protected static BlockState computeState(BlockState state, BlockGetter level, BlockPos pos) {
boolean aboveIsSame = level.getBlockState(pos.above()).is(state.getBlock());
boolean belowIsSame = level.getBlockState(pos.below()).is(state.getBlock());
Direction growthDirection = state.getValue(GROWTH_DIRECTION);
boolean topCap = growthDirection == Direction.UP && !aboveIsSame;
boolean bottomCap = growthDirection == Direction.DOWN && !belowIsSame;
return state.setValue(TOP_CAP, topCap).setValue(BOTTOM_CAP, bottomCap);
}
protected int maxDistance() {
return MAX_DISTANCE;
}
@Override
protected boolean isRandomlyTicking(BlockState state) {
return isTip(state) && state.getValue(DISTANCE) < maxDistance();
}
@Override
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (random.nextInt(10) == 0) {
grow(level, pos, state);
}
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
return isTip(state) && state.getValue(DISTANCE) < maxDistance() && level.getBlockState(pos.relative(state.getValue(GROWTH_DIRECTION))).isAir();
}
@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) {
grow(level, pos, state);
}
private void grow(ServerLevel level, BlockPos pos, BlockState state) {
Direction direction = state.getValue(GROWTH_DIRECTION);
BlockPos targetPos = pos.relative(direction);
if (!level.getBlockState(targetPos).isAir()) {
return;
}
BlockState grownState = this.defaultBlockState()
.setValue(GROWTH_DIRECTION, direction)
.setValue(DISTANCE, state.getValue(DISTANCE) + 1);
level.setBlock(targetPos, grownState, Block.UPDATE_ALL);
BlockState posState = level.getBlockState(pos);
if (posState.is(this)) {
level.setBlock(pos, computeState(posState, level, pos), Block.UPDATE_CLIENTS);
}
BlockState targetState = level.getBlockState(targetPos);
if (targetState.is(this)) {
level.setBlock(targetPos, computeState(targetState, level, targetPos), Block.UPDATE_CLIENTS);
}
}
protected static boolean isTip(BlockState state) {
return state.getValue(GROWTH_DIRECTION) == Direction.DOWN ? state.getValue(BOTTOM_CAP) : state.getValue(TOP_CAP);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(GROWTH_DIRECTION, TOP_CAP, BOTTOM_CAP, DISTANCE);
}
}
@@ -1,41 +1,18 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import java.util.function.Supplier;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.GrowingPlantHeadBlock;
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.VoxelShape;
public class SpiralingVinesBlock extends GrowingPlantHeadBlock {
public class SpiralingVinesBlock extends SimpleDirectionalVineBlock {
public static final MapCodec<SpiralingVinesBlock> CODEC = simpleCodec(SpiralingVinesBlock::new);
public static final VoxelShape SHAPE = Block.box(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D);
public static Supplier<Block> BODY_BLOCK;
public SpiralingVinesBlock(BlockBehaviour.Properties properties) {
super(properties, Direction.UP, SHAPE, false, 0.1D);
super(properties);
}
@Override
public MapCodec<SpiralingVinesBlock> codec() {
public MapCodec<? extends BushBlock> codec() {
return CODEC;
}
@Override
protected int getBlocksToGrowWhenBonemealed(RandomSource random) {
return 1;
}
@Override
protected boolean canGrowInto(BlockState state) {
return state.isAir();
}
@Override
protected Block getBodyBlock() {
return BODY_BLOCK.get();
}
}
@@ -1,28 +0,0 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import java.util.function.Supplier;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.GrowingPlantBodyBlock;
import net.minecraft.world.level.block.GrowingPlantHeadBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
public class SpiralingVinesPlantBlock extends GrowingPlantBodyBlock {
public static final MapCodec<SpiralingVinesPlantBlock> CODEC = simpleCodec(SpiralingVinesPlantBlock::new);
public static Supplier<GrowingPlantHeadBlock> HEAD_BLOCK;
public SpiralingVinesPlantBlock(BlockBehaviour.Properties properties) {
super(properties, Direction.UP, SpiralingVinesBlock.SHAPE, false);
}
@Override
public MapCodec<SpiralingVinesPlantBlock> codec() {
return CODEC;
}
@Override
protected GrowingPlantHeadBlock getHeadBlock() {
return HEAD_BLOCK.get();
}
}
@@ -146,12 +146,11 @@ public class TyphoniteSpikeBlock extends PointedDripstoneBlock {
}
DripstoneThickness forwardThickness = forwardState.getValue(THICKNESS);
if (forwardThickness != DripstoneThickness.TIP && forwardThickness != DripstoneThickness.TIP_MERGE) {
BlockState backState = level.getBlockState(pos.relative(opposite));
return !isTyphoniteSpikeWithDirection(backState, direction) ? DripstoneThickness.BASE : DripstoneThickness.MIDDLE;
if (forwardThickness == DripstoneThickness.TIP || forwardThickness == DripstoneThickness.TIP_MERGE) {
return DripstoneThickness.MIDDLE;
}
return DripstoneThickness.FRUSTUM;
return DripstoneThickness.BASE;
}
private static boolean isValidPointedDripstonePlacement(LevelReader level, BlockPos pos, Direction direction) {
@@ -3,24 +3,35 @@ 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.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
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.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.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class WhirlflowerBlock extends BushBlock {
public static final MapCodec<WhirlflowerBlock> CODEC = Block.simpleCodec(WhirlflowerBlock::new);
private static final VoxelShape SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 13.0D, 13.0D);
public static final DirectionProperty VERTICAL_DIRECTION = BlockStateProperties.VERTICAL_DIRECTION;
private static final VoxelShape UP_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 13.0D, 13.0D);
private static final VoxelShape DOWN_SHAPE = Block.box(3.0D, 3.0D, 3.0D, 13.0D, 16.0D, 13.0D);
private static final ResourceLocation VEINED_TYPHONITE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "veined_typhonite");
private static final ResourceLocation VERDANT_NYXIUM_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "verdant_nyxium");
public WhirlflowerBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(VERTICAL_DIRECTION, Direction.UP));
}
@Override
@@ -30,16 +41,46 @@ public class WhirlflowerBlock extends BushBlock {
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
return state.getValue(VERTICAL_DIRECTION) == Direction.DOWN ? DOWN_SHAPE : UP_SHAPE;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(
VERTICAL_DIRECTION,
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
);
}
@Override
protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
return BuiltInRegistries.BLOCK.getKey(state.getBlock()).equals(VEINED_TYPHONITE_ID);
ResourceLocation id = BuiltInRegistries.BLOCK.getKey(state.getBlock());
return id.equals(VEINED_TYPHONITE_ID) || id.equals(VERDANT_NYXIUM_ID);
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return this.mayPlaceOn(level.getBlockState(pos.below()), level, pos.below());
Direction growthDirection = state.getValue(VERTICAL_DIRECTION);
BlockPos supportPos = pos.relative(growthDirection.getOpposite());
return this.mayPlaceOn(level.getBlockState(supportPos), level, supportPos);
}
@Override
protected BlockState updateShape(
BlockState state,
Direction direction,
BlockState neighborState,
LevelAccessor level,
BlockPos pos,
BlockPos neighborPos
) {
return state.canSurvive(level, pos)
? super.updateShape(state, direction, neighborState, level, pos, neighborPos)
: Blocks.AIR.defaultBlockState();
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(VERTICAL_DIRECTION);
}
}
@@ -0,0 +1,48 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.item.ManticoreWingsItem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.ElytraModel;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.model.geom.ModelLayers;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.renderer.entity.RenderLayerParent;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
public final class ManticoreWingsLayer extends RenderLayer<net.minecraft.client.player.AbstractClientPlayer, PlayerModel<net.minecraft.client.player.AbstractClientPlayer>> {
private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/models/armor/manticore_wings.png");
private final ElytraModel<net.minecraft.client.player.AbstractClientPlayer> model;
public ManticoreWingsLayer(RenderLayerParent<net.minecraft.client.player.AbstractClientPlayer, PlayerModel<net.minecraft.client.player.AbstractClientPlayer>> renderer) {
super(renderer);
this.model = new ElytraModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.ELYTRA));
}
@Override
public void render(PoseStack poseStack, MultiBufferSource buffer, int packedLight,
net.minecraft.client.player.AbstractClientPlayer player, float limbSwing,
float limbSwingAmount, float partialTick, float ageInTicks,
float netHeadYaw, float headPitch) {
if (!ManticoreWingsItem.isWearingManticoreWings(player) || player.isInvisible()) {
return;
}
ItemStack chestStack = player.getItemBySlot(EquipmentSlot.CHEST);
poseStack.pushPose();
this.getParentModel().copyPropertiesTo(this.model);
if (player.isCrouching()) {
poseStack.translate(0.0F, 0.0F, 0.125F);
}
this.model.setupAnim(player, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch);
VertexConsumer consumer = ItemRenderer.getFoilBufferDirect(buffer, RenderType.armorCutoutNoCull(TEXTURE), false, chestStack.hasFoil());
this.model.renderToBuffer(poseStack, consumer, packedLight, OverlayTexture.NO_OVERLAY);
poseStack.popPose();
}
}
@@ -30,6 +30,8 @@ public class WindVortexRenderer extends EntityRenderer<WindVortexEntity> {
float height = entity.getVortexHeight();
float topRadius = entity.getTopRadius();
float age = entity.tickCount + partialTick;
float fadeIn = Mth.clamp(age / 8.0F, 0.0F, 1.0F);
float alphaMultiplier = fadeIn * entity.getFadeOutProgress(partialTick);
WindVortexEntity.Basis basis = entity.basis();
boolean pull = entity.getMode() == WindVortexEntity.VortexMode.LENS_PULL;
boolean push = entity.getMode() == WindVortexEntity.VortexMode.LENS_PUSH;
@@ -41,7 +43,7 @@ public class WindVortexRenderer extends EntityRenderer<WindVortexEntity> {
float shapeProgress = pull ? 1.0F - progress : progress;
float radius = Mth.lerp(shapeProgress, BASE_RADIUS, topRadius);
float ringSpin = spinDirection * age * (0.13F + progress * 0.045F) + ring * 0.47F;
float alpha = 1.0F;
float alpha = alphaMultiplier;
float thickness = 0.025F + radius * 0.012F;
float arcLength = 0.34F + progress * 0.08F;
int red = push ? 70 : 64;
@@ -213,8 +213,15 @@ public final class CreativeTabContents {
entry("polished_typhonite_stairs", "building_blocks"),
entry("polished_typhonite_slab", "building_blocks"),
entry("polished_typhonite_wall", "building_blocks"),
entry("verdant_nyxium", "natural_blocks"),
entry("spiraling_vines", "natural_blocks"),
entry("gorevine", "natural_blocks"),
entry("whirlflower", "natural_blocks"),
entry("nadir_nyxium", "natural_blocks"),
entry("nadir_fern", "natural_blocks"),
entry("large_nadir_fern", "natural_blocks"),
entry("duskbell", "natural_blocks"),
entry("typhonite_pillar", "building_blocks"),
entry("antigravel", "natural_blocks"),
entry("budding_blood_crystal", "building_blocks"),
entry("blood_crystal_block", "building_blocks"),
@@ -432,6 +439,7 @@ public final class CreativeTabContents {
entry("royal_guardian_boots", "combat"),
entry("royal_guardian_shield", "combat"),
entry("royal_assailant_battleaxe", "combat"),
entry("royal_assailant_staff", "combat"),
entry("royal_assailant_helmet", "combat"),
entry("royal_assailant_chestplate", "combat"),
entry("royal_assailant_leggings", "combat"),
@@ -451,6 +459,7 @@ public final class CreativeTabContents {
entry("scorpion_whip", "combat"),
entry("fallen_king_crown", "combat"),
entry("brutalfly_elytra", "combat"),
entry("manticore_wings", "combat"),
entry("jumpy_boots", "combat"),
entry("raw_uranium_scrap", "ingredients"),
entry("raw_titanium_scrap", "ingredients"),
@@ -483,6 +492,7 @@ public final class CreativeTabContents {
entry("jerry_nucleus", "ingredients"),
entry("stink_bug", "ingredients"),
entry("brutalfly_wing", "ingredients"),
entry("manticore_wing", "ingredients"),
entry("primordial_scute", "ingredients"),
entry("triffid_goo", "ingredients"),
entry("vortex_charge", "ingredients"),
@@ -505,6 +515,7 @@ public final class CreativeTabContents {
entry("raw_bug_meat", "ingredients"),
entry("cooked_bug_meat", "ingredients"),
entry("broodfruit", "ingredients"),
entry("broodfruit_cobbler", "ingredients"),
entry("cheep", "ingredients"),
entry("cooked_cheep", "ingredients"),
entry("root_beer", "ingredients"),
@@ -53,6 +53,8 @@ import software.bernie.geckolib.util.GeckoLibUtil;
public class ManticoreEntity extends Monster implements GeoEntity {
private static final String SUMMONER_KEY = "QueenSummoner";
private static final String PLAYER_SUMMONER_KEY = "PlayerSummoner";
private static final String PLAYER_SUMMON_LIFETIME_KEY = "PlayerSummonLifetime";
private static final int LOST_SUMMONER_GRACE_TICKS = 200;
private static final int TAKEOFF_COOLDOWN_TICKS = 40;
private static final int STING_COOLDOWN_TICKS = 60;
@@ -72,6 +74,10 @@ public class ManticoreEntity extends Monster implements GeoEntity {
@Nullable
private UUID summonerId;
@Nullable
private UUID playerSummonerId;
private int playerSummonLifetime;
private int playerSummonAge;
private int lostSummonerTicks;
private int takeoffCooldown;
private int stingCooldown;
@@ -124,7 +130,7 @@ public class ManticoreEntity extends Monster implements GeoEntity {
this.goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 10.0F));
this.goalSelector.addGoal(5, new RandomLookAroundGoal(this));
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::canTargetEntity));
}
@Override
@@ -135,6 +141,16 @@ public class ManticoreEntity extends Monster implements GeoEntity {
public void markQueenSummoned(UUID queenId) {
this.summonerId = queenId;
this.playerSummonerId = null;
this.lostSummonerTicks = 0;
this.setPersistenceRequired();
}
public void markPlayerSummoned(UUID playerId, int lifetimeTicks) {
this.playerSummonerId = playerId;
this.summonerId = null;
this.playerSummonLifetime = lifetimeTicks;
this.playerSummonAge = 0;
this.lostSummonerTicks = 0;
this.setPersistenceRequired();
}
@@ -147,6 +163,18 @@ public class ManticoreEntity extends Monster implements GeoEntity {
return this.summonerId != null;
}
public boolean isPlayerSummoned() {
return this.playerSummonerId != null;
}
@Override
public boolean isAlliedTo(Entity entity) {
if (entity != null && this.playerSummonerId != null && entity.getUUID().equals(this.playerSummonerId)) {
return true;
}
return super.isAlliedTo(entity);
}
public static int countSummonedBy(ServerLevel level, UUID queenId) {
int count = 0;
for (Entity entity : level.getAllEntities()) {
@@ -164,7 +192,7 @@ public class ManticoreEntity extends Monster implements GeoEntity {
@Override
public boolean requiresCustomPersistence() {
return this.summonerId != null || super.requiresCustomPersistence();
return this.summonerId != null || this.playerSummonerId != null || super.requiresCustomPersistence();
}
@Override
@@ -189,6 +217,8 @@ public class ManticoreEntity extends Monster implements GeoEntity {
this.tickQueenSummonCleanup();
this.tickPlayerSummon();
LivingEntity target = this.getTarget();
if (target != null && target.isAlive()) {
if (this.onGround() && this.takeoffCooldown <= 0 && this.distanceToSqr(target) > 6.0D) {
@@ -200,6 +230,50 @@ public class ManticoreEntity extends Monster implements GeoEntity {
}
}
private void tickPlayerSummon() {
if (this.playerSummonerId == null || !(this.level() instanceof ServerLevel serverLevel)) {
return;
}
if (++this.playerSummonAge >= this.playerSummonLifetime) {
this.discard();
return;
}
Entity entity = serverLevel.getEntity(this.playerSummonerId);
if (!(entity instanceof Player player) || !player.isAlive()) {
this.discard();
return;
}
LivingEntity target = player.getLastHurtMob();
if (target == null || !target.isAlive() || !this.canTargetEntity(target)) {
target = player.getLastHurtByMob();
}
if (target != null && target.isAlive() && this.canTargetEntity(target)) {
this.setTarget(target);
}
}
private boolean canTargetEntity(LivingEntity target) {
if (target == null || target == this || this.isAlliedTo(target) || target.isAlliedTo(this)) {
return false;
}
if (this.playerSummonerId != null) {
if (!(this.level() instanceof ServerLevel serverLevel)) {
return false;
}
Entity entity = serverLevel.getEntity(this.playerSummonerId);
if (!(entity instanceof Player player)) {
return false;
}
return target == player.getLastHurtMob() || target == player.getLastHurtByMob();
}
return true;
}
@Override
public void setTarget(@Nullable LivingEntity target) {
super.setTarget(target != null && !this.canTargetEntity(target) ? null : target);
}
private void tickQueenSummonCleanup() {
if (this.summonerId == null || !(this.level() instanceof ServerLevel serverLevel)) {
return;
@@ -307,12 +381,19 @@ public class ManticoreEntity extends Monster implements GeoEntity {
if (this.summonerId != null) {
tag.putUUID(SUMMONER_KEY, this.summonerId);
}
if (this.playerSummonerId != null) {
tag.putUUID(PLAYER_SUMMONER_KEY, this.playerSummonerId);
tag.putInt(PLAYER_SUMMON_LIFETIME_KEY, this.playerSummonLifetime);
}
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.summonerId = tag.hasUUID(SUMMONER_KEY) ? tag.getUUID(SUMMONER_KEY) : null;
this.playerSummonerId = tag.hasUUID(PLAYER_SUMMONER_KEY) ? tag.getUUID(PLAYER_SUMMONER_KEY) : null;
this.playerSummonLifetime = tag.getInt(PLAYER_SUMMON_LIFETIME_KEY);
this.playerSummonAge = 0;
this.lostSummonerTicks = 0;
}
@@ -3,7 +3,6 @@ package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.content.block.AntimetalScaffoldingBlock;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.syncher.EntityDataAccessor;
@@ -13,7 +12,6 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MoverType;
@@ -38,8 +36,6 @@ public class UpwardFallingBlockEntity extends Entity {
private static final double DRAG = 0.98;
private static final int MAX_TICKS = 200;
private static final int MAX_LIVE_PER_LEVEL = 200;
private static final double PLAYER_ACTIVATION_RADIUS = 128.0;
private static final int INSTANT_MOVE_MAX_DISTANCE = 48;
private static final double MIN_TRAVEL_FOR_DAMAGE = 1.5;
private static final Object2IntOpenHashMap<ResourceKey<Level>> LIVE_COUNT = new Object2IntOpenHashMap<>();
@@ -87,10 +83,7 @@ public class UpwardFallingBlockEntity extends Entity {
}
}
boolean noPlayerNearby = serverLevel.getNearestPlayer(
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, PLAYER_ACTIVATION_RADIUS, EntitySelector.NO_SPECTATORS) == null;
if (live >= MAX_LIVE_PER_LEVEL || noPlayerNearby) {
resolveInstantly(serverLevel, pos, state, breaksOnLeaves);
if (live >= MAX_LIVE_PER_LEVEL) {
return;
}
@@ -105,46 +98,6 @@ public class UpwardFallingBlockEntity extends Entity {
}
}
private static void resolveInstantly(ServerLevel level, BlockPos pos, BlockState state, boolean breaksOnLeaves) {
level.removeBlock(pos, false);
BlockPos.MutableBlockPos rest = pos.mutable();
for (int i = 0; i < INSTANT_MOVE_MAX_DISTANCE; i++) {
BlockPos above = rest.above();
if (above.getY() >= level.getMaxBuildHeight()) {
break;
}
BlockState aboveState = level.getBlockState(above);
if (aboveState.getBlock() instanceof AntimetalScaffoldingBlock) {
break;
}
if (breaksOnLeaves && aboveState.is(BlockTags.LEAVES)) {
dropAsItem(level, rest, state);
return;
}
if (!aboveState.canBeReplaced()) {
break;
}
rest.move(Direction.UP);
}
if (level.getBlockState(rest).canBeReplaced()) {
level.setBlock(rest, state, Block.UPDATE_ALL);
} else {
dropAsItem(level, rest, state);
}
}
private static void dropAsItem(Level level, BlockPos pos, BlockState state) {
if (state.isAir()) {
return;
}
ItemStack drop = new ItemStack(state.getBlock().asItem());
if (!drop.isEmpty()) {
Block.popResource(level, pos, drop);
}
}
@Override
public void remove(Entity.RemovalReason reason) {
if (this.counted) {
@@ -1032,7 +1032,7 @@ public class NightmareEntity extends Monster implements GeoEntity {
}
}
if (this.level() instanceof ServerLevel serverLevel) {
serverLevel.sendParticles(ParticleTypes.SOUL_FIRE_FLAME, this.getX(), this.getY() + 1.6D, this.getZ(), 12, 0.9D, 0.35D, 0.9D, 0.02D);
serverLevel.sendParticles(AntarchyObjects.NIGHTMARE_FIRE_FLAME.get(), this.getX(), this.getY() + 1.6D, this.getZ(), 12, 0.9D, 0.35D, 0.9D, 0.02D);
serverLevel.sendParticles(ParticleTypes.SMOKE, this.getX(), this.getY() + 1.6D, this.getZ(), 8, 0.7D, 0.25D, 0.7D, 0.03D);
}
}
@@ -1077,7 +1077,7 @@ public class NightmareEntity extends Monster implements GeoEntity {
}
}
if (this.level() instanceof ServerLevel serverLevel) {
serverLevel.sendParticles(ParticleTypes.SOUL_FIRE_FLAME, this.portalExitPos.x, this.portalExitPos.y + 1.0D, this.portalExitPos.z, 18, 0.9D, 0.45D, 0.9D, 0.02D);
serverLevel.sendParticles(AntarchyObjects.NIGHTMARE_FIRE_FLAME.get(), this.portalExitPos.x, this.portalExitPos.y + 1.0D, this.portalExitPos.z, 18, 0.9D, 0.45D, 0.9D, 0.02D);
serverLevel.sendParticles(ParticleTypes.SMOKE, this.portalExitPos.x, this.portalExitPos.y + 1.0D, this.portalExitPos.z, 14, 0.8D, 0.3D, 0.8D, 0.03D);
}
}
@@ -80,6 +80,10 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
private UUID queenOwnerId;
private int queenManticoreCount;
private boolean queenManticoreSpawned;
@Nullable
private UUID staffOwnerId;
private int staffManticoreCount;
private boolean staffManticoreSpawned;
public DimensionalTearEntity(EntityType<? extends DimensionalTearEntity> entityType, Level level) {
super(entityType, level);
@@ -104,6 +108,14 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
return tear;
}
public static DimensionalTearEntity createStaffSummonTear(ServerLevel level, Vec3 pos, float yaw,
int lifetimeTicks, UUID playerId, int count) {
DimensionalTearEntity tear = create(level, pos, yaw, lifetimeTicks);
tear.staffOwnerId = playerId;
tear.staffManticoreCount = Math.max(1, count);
return tear;
}
public void linkTo(DimensionalTearEntity other) {
this.linkedTearId = other.getUUID();
this.linkedFallbackPos = other.position();
@@ -139,14 +151,20 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
this.spawnQueenManticores((ServerLevel) this.level());
}
if (this.staffOwnerId != null && this.ageTicks == OPENING_ANIMATION_TICKS) {
this.spawnStaffManticores((ServerLevel) this.level());
}
DimensionalTearEntity linked = this.findLinkedTear();
if (linked == null || !linked.isAlive()) {
this.discard();
return;
}
this.tickMobEvent((ServerLevel) this.level());
this.tickTeleport(linked);
if (this.staffOwnerId == null && this.ageTicks >= OPENING_ANIMATION_TICKS) {
this.tickMobEvent((ServerLevel) this.level());
this.tickTeleport(linked);
}
}
private void tickMobEvent(ServerLevel level) {
@@ -232,6 +250,37 @@ public class DimensionalTearEntity extends Entity implements GeoEntity {
}
}
private void spawnStaffManticores(ServerLevel level) {
if (this.staffManticoreSpawned) {
return;
}
this.staffManticoreSpawned = true;
Entity owner = level.getEntity(this.staffOwnerId);
if (!(owner instanceof net.minecraft.world.entity.player.Player player) || !player.isAlive()) {
return;
}
for (int i = 0; i < this.staffManticoreCount; i++) {
ManticoreEntity manticore = AntarchyObjects.MANTICORE.get().create(level);
if (manticore == null) {
continue;
}
Vec3 exit = this.exitPosition().add(0.0D, 0.8D + i * 0.35D, 0.0D);
manticore.moveTo(exit.x, exit.y, exit.z, this.getYRot(), 0.0F);
if (!level.noCollision(manticore)) {
manticore.discard();
continue;
}
manticore.finalizeSpawn(level, level.getCurrentDifficultyAt(BlockPos.containing(exit)), MobSpawnType.MOB_SUMMONED, null);
manticore.markPlayerSummoned(player.getUUID(), 20 * 30);
if (player.getLastHurtMob() != null && player.getLastHurtMob().isAlive()) {
manticore.setTarget(player.getLastHurtMob());
}
manticore.setDeltaMovement(this.getViewVector(1.0F).scale(0.2D).add(0.0D, 0.08D, 0.0D));
manticore.setNoGravity(true);
level.addFreshEntity(manticore);
}
}
public static void discardQueenOwnedTears(ServerLevel level, UUID queenId) {
for (DimensionalTearEntity tear : level.getEntitiesOfClass(DimensionalTearEntity.class,
new AABB(-3.0E7D, level.getMinBuildHeight(), -3.0E7D, 3.0E7D, level.getMaxBuildHeight(), 3.0E7D),
@@ -1,6 +1,7 @@
package com.craisinlord.antarchy.content.entity.vortex;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
@@ -95,6 +96,9 @@ public class VortexEntity extends Monster implements GeoEntity {
public static boolean canSpawn(EntityType<VortexEntity> entityType, ServerLevelAccessor level,
MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (pos.getY() < ThoraxisUndersideManager.GRAVITY_FLIP_Y && ThoraxisUndersideManager.isThoraxis(level.getLevel())) {
return false;
}
return level.getDifficulty() != Difficulty.PEACEFUL
&& level.isEmptyBlock(pos)
&& level.isEmptyBlock(pos.above())
@@ -241,15 +245,15 @@ public class VortexEntity extends Monster implements GeoEntity {
if (!(this.level() instanceof ServerLevel serverLevel) || windVortexTypeSupplier == null) {
return;
}
Vec3 targetPos = target.position();
Vec3 fromMob = targetPos.subtract(this.position());
Vec3 drift = fromMob.lengthSqr() > 1.0E-6D ? fromMob.normalize().scale(0.045D) : this.getViewVector(0.0F).scale(0.045D);
Vec3 position = targetPos.add(
(this.random.nextDouble() - 0.5D) * 1.6D,
0.05D,
(this.random.nextDouble() - 0.5D) * 1.6D
);
WindVortexEntity vortex = WindVortexEntity.create(serverLevel, windVortexTypeSupplier.get(), position, drift, this, true);
Vec3 targetPos = target.getBoundingBox().getCenter();
Vec3 origin = this.getEyePosition().add(this.getViewVector(1.0F).scale(0.8D));
Vec3 direction = targetPos.subtract(origin);
double distance = direction.length();
Vec3 drift = distance > 1.0E-6D ? direction.scale(0.24D / distance) : this.getViewVector(1.0F).scale(0.24D);
WindVortexEntity vortex = WindVortexEntity.create(serverLevel, windVortexTypeSupplier.get(), origin, Vec3.ZERO, this, true);
vortex.setTravel(drift);
vortex.setTravelDuration(Mth.clamp((int) Math.ceil(distance / drift.length()), 8, 36));
vortex.setHoming(false);
serverLevel.addFreshEntity(vortex);
serverLevel.playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.BREEZE_SHOOT, this.getSoundSource(), 1.0F, 0.9F);
}
@@ -46,6 +46,8 @@ public class WindVortexEntity extends Entity {
SynchedEntityData.defineId(WindVortexEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> AXIS_Z =
SynchedEntityData.defineId(WindVortexEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Boolean> FADING_OUT =
SynchedEntityData.defineId(WindVortexEntity.class, EntityDataSerializers.BOOLEAN);
private static final String AGE_KEY = "Age";
private static final String DURATION_KEY = "Duration";
@@ -67,6 +69,7 @@ public class WindVortexEntity extends Entity {
private static final double BASE_RADIUS = 0.35D;
private static final double DRIFT_FRICTION = 0.96D;
private static final int FADE_OUT_TICKS = 8;
public enum VortexMode {
UPWARD,
@@ -83,6 +86,8 @@ public class WindVortexEntity extends Entity {
private Vec3 travelVelocity = Vec3.ZERO;
private boolean travelling = false;
private boolean homing = false;
private int travelTicksRemaining;
private int fadeOutTicksRemaining;
private final Map<UUID, Double> carriedProgress = new HashMap<>();
@Nullable
private UUID ownerUuid;
@@ -119,19 +124,30 @@ public class WindVortexEntity extends Entity {
builder.define(AXIS_X, 0.0F);
builder.define(AXIS_Y, 1.0F);
builder.define(AXIS_Z, 0.0F);
builder.define(FADING_OUT, false);
}
@Override
public void tick() {
super.tick();
boolean lensVortex = this.getMode() != VortexMode.UPWARD;
if (!lensVortex && (this.isInLava() || this.isInsideSolidBlock())) {
this.fadeOut();
if (this.level().isClientSide) {
if (this.fadeOutTicksRemaining > 0) {
this.fadeOutTicksRemaining--;
}
return;
}
if (this.level().isClientSide) {
if (this.isFadingOut()) {
if (--this.fadeOutTicksRemaining <= 0) {
this.discard();
}
return;
}
boolean lensVortex = this.getMode() != VortexMode.UPWARD;
if (!lensVortex && (this.isInLava() || this.isInsideSolidBlock())) {
this.fadeOut();
return;
}
@@ -155,6 +171,12 @@ public class WindVortexEntity extends Entity {
}
this.setDeltaMovement(this.travelVelocity);
this.move(net.minecraft.world.entity.MoverType.SELF, this.travelVelocity);
if (--this.travelTicksRemaining <= 0) {
this.travelling = false;
this.noPhysics = false;
this.travelVelocity = Vec3.ZERO;
this.setDeltaMovement(Vec3.ZERO);
}
} else {
Vec3 drift = this.getDeltaMovement().multiply(DRIFT_FRICTION, DRIFT_FRICTION, DRIFT_FRICTION);
this.setDeltaMovement(drift);
@@ -207,15 +229,17 @@ public class WindVortexEntity extends Entity {
}
private void fadeOut() {
if (!this.level().isClientSide) {
if (!this.level().isClientSide && !this.isFadingOut()) {
this.entityData.set(FADING_OUT, true);
this.fadeOutTicksRemaining = FADE_OUT_TICKS;
this.level().broadcastEntityEvent(this, (byte) 60);
this.discard();
}
}
@Override
public void handleEntityEvent(byte id) {
if (id == 60) {
this.fadeOutTicksRemaining = FADE_OUT_TICKS;
return;
}
super.handleEntityEvent(id);
@@ -526,9 +550,14 @@ public class WindVortexEntity extends Entity {
public void setTravel(Vec3 velocity) {
this.travelVelocity = velocity;
this.travelling = true;
this.travelTicksRemaining = 24;
this.noPhysics = true;
}
public void setTravelDuration(int ticks) {
this.travelTicksRemaining = Math.max(1, ticks);
}
public void setHoming(boolean homing) {
this.homing = homing;
}
@@ -558,6 +587,17 @@ public class WindVortexEntity extends Entity {
this.entityData.set(DAMAGING, damaging);
}
public boolean isFadingOut() {
return this.entityData.get(FADING_OUT);
}
public float getFadeOutProgress(float partialTick) {
if (!this.isFadingOut()) {
return 1.0F;
}
return Mth.clamp((this.fadeOutTicksRemaining - partialTick) / (float) FADE_OUT_TICKS, 0.0F, 1.0F);
}
@Override
protected void readAdditionalSaveData(CompoundTag tag) {
this.age = tag.getInt(AGE_KEY);
@@ -0,0 +1,62 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.ChatFormatting;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
public final class ManticoreWingsItem extends ElytraItem {
private static final ResourceLocation REPAIR_MATERIAL_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "manticore_wing");
private static final int DAMAGE_COOLDOWN_TICKS = 10;
private static final float COLLISION_DAMAGE = 8.0F;
private static final Map<UUID, Long> LAST_COLLISION_DAMAGE = new ConcurrentHashMap<>();
public ManticoreWingsItem(Properties properties) {
super(properties);
}
@Override
public boolean isValidRepairItem(ItemStack toRepair, ItemStack repair) {
return BuiltInRegistries.ITEM.getOptional(REPAIR_MATERIAL_ID).map(repair::is).orElse(false);
}
@Override
public void inventoryTick(ItemStack stack, Level level, net.minecraft.world.entity.Entity entity, int slotId, boolean selected) {
super.inventoryTick(stack, level, entity, slotId, selected);
if (!(entity instanceof Player player) || level.isClientSide || !player.isFallFlying()
|| player.getItemBySlot(EquipmentSlot.CHEST) != stack) {
return;
}
long gameTime = level.getGameTime();
for (LivingEntity target : level.getEntitiesOfClass(LivingEntity.class, player.getBoundingBox().inflate(0.45D), living ->
living != player && living.isAlive() && !player.isAlliedTo(living) && !living.isAlliedTo(player))) {
long availableAt = LAST_COLLISION_DAMAGE.getOrDefault(target.getUUID(), 0L);
if (availableAt <= gameTime && target.hurt(level.damageSources().playerAttack(player), COLLISION_DAMAGE)) {
LAST_COLLISION_DAMAGE.put(target.getUUID(), gameTime + DAMAGE_COOLDOWN_TICKS);
}
}
}
public static boolean isWearingManticoreWings(LivingEntity entity) {
return entity != null && entity.getItemBySlot(EquipmentSlot.CHEST).getItem() instanceof ManticoreWingsItem;
}
@Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.manticore_wings").withStyle(ChatFormatting.GRAY));
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
}
@@ -0,0 +1,106 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
import java.util.List;
import java.util.function.Consumer;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import software.bernie.geckolib.animatable.GeoItem;
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
public final class RoyalAssailantStaffItem extends Item implements GeoItem {
private static final int COOLDOWN_TICKS = 20 * 30;
private static final int TEAR_LIFETIME_TICKS = 20 * 6;
private static final ResourceLocation MODEL_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "geo/royal_assailant_staff.geo.json");
private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/models/item/royal_assailant_staff.png");
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/royal_assailant_staff.animation.json");
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
public RoyalAssailantStaffItem(Properties properties) {
super(properties);
GeoItem.registerSyncedAnimatable(this);
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack stack = player.getItemInHand(hand);
if (player.getCooldowns().isOnCooldown(this)) {
return InteractionResultHolder.fail(stack);
}
if (level.isClientSide) {
return InteractionResultHolder.sidedSuccess(stack, true);
}
ServerLevel serverLevel = (ServerLevel) level;
Vec3 forward = player.getLookAngle().normalize();
Vec3 perpendicular = new Vec3(-forward.z, 0.0D, forward.x);
if (perpendicular.lengthSqr() < 0.01D) {
perpendicular = new Vec3(1.0D, 0.0D, 0.0D);
}
perpendicular = perpendicular.normalize().scale(4.0D);
Vec3 center = player.position().add(0.0D, player.getBbHeight() + 2.0D, 0.0D);
DimensionalTearEntity first = DimensionalTearEntity.createStaffSummonTear(serverLevel, center.add(perpendicular), player.getYRot(), TEAR_LIFETIME_TICKS, player.getUUID(), 2);
DimensionalTearEntity second = DimensionalTearEntity.createStaffSummonTear(serverLevel, center.subtract(perpendicular), player.getYRot() + 180.0F, TEAR_LIFETIME_TICKS, player.getUUID(), 2);
first.linkTo(second);
second.linkTo(first);
serverLevel.addFreshEntity(first);
serverLevel.addFreshEntity(second);
player.getCooldowns().addCooldown(this, COOLDOWN_TICKS);
triggerAnim(player, GeoItem.getOrAssignId(stack, serverLevel), "use_controller", "use");
level.playSound(null, player.blockPosition(), SoundEvents.RESPAWN_ANCHOR_CHARGE, SoundSource.HOSTILE, 1.0F, 0.75F);
return InteractionResultHolder.success(stack);
}
@Override
public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
consumer.accept(new GeoRenderProvider() {
private AnimatedHeldItemRenderer<RoyalAssailantStaffItem> renderer;
@Override
public net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
if (this.renderer == null) {
this.renderer = new AnimatedHeldItemRenderer<>(new ResourceBackedGeoItemModel<>(MODEL_LOCATION, TEXTURE_LOCATION, ANIMATION_LOCATION));
}
return this.renderer;
}
});
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache;
}
@Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.royal_assailant_staff").withStyle(ChatFormatting.LIGHT_PURPLE));
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "idle_controller", state ->
state.setAndContinue(RawAnimation.begin().thenLoop("idle"))));
controllers.add(new AnimationController<>(this, "use_controller", state -> PlayState.STOP)
.triggerableAnim("use", RawAnimation.begin().thenPlay("use")));
}
}
@@ -22,9 +22,10 @@ public final class ThoraxisBiomeSource extends BiomeSource {
private static final int REGION_DETAIL_SCALE = 41;
private static final int VERTICAL_NOISE_SCALE = 8;
private static final double DUNES_SELECTOR_CUTOFF = 0.38D;
private static final double DREAM_DUNES_SELECTOR_CUTOFF = 0.12D;
private static final double HILLS_SELECTOR_CUTOFF = 0.24D;
private static final double NADIR_FOREST_SELECTOR_MIN = 0.42D;
private static final double NADIR_FOREST_SELECTOR_MAX = 0.66D;
private static final double NADIR_FOREST_SELECTOR_MIN = 0.54D;
private static final double NADIR_FOREST_SELECTOR_MAX = 0.56D;
private static final ResourceKey<Biome> NIGHTMARE_WASTES = ResourceKey.create(
Registries.BIOME,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "nightmare_wastes")
@@ -166,7 +167,11 @@ public final class ThoraxisBiomeSource extends BiomeSource {
return this.lucidPoolsBiome;
}
if (this.vortexExpanseBiome != null && y <= this.dreamDunesMaxQuartY && dreamDunesSelector <= DUNES_SELECTOR_CUTOFF) {
if (this.dreamDunesBiome != null && y <= this.dreamDunesMaxQuartY && dreamDunesSelector <= DREAM_DUNES_SELECTOR_CUTOFF) {
return this.dreamDunesBiome;
}
if (this.vortexExpanseBiome != null && dreamDunesSelector <= DUNES_SELECTOR_CUTOFF) {
return this.vortexExpanseBiome;
}
@@ -6,7 +6,9 @@ import com.craisinlord.antarchy.content.block.DreamSandBlock;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.minecraft.core.BlockPos;
@@ -25,11 +27,15 @@ public final class ThoraxisUndersideManager {
public static final int GRAVITY_FLIP_Y = 0;
private static final ResourceLocation THORAXIS_DIMENSION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "thoraxis");
private static final AntarchyGravityTransition TRANSITION = new AntarchyGravityTransition(12);
private static final int EFFECT_DURATION_TICKS = 40;
private static final int EFFECT_DURATION_TICKS = 50;
private static final int EFFECT_REFRESH_THRESHOLD_TICKS = 10;
private static final int MAX_FLIPS_PER_TICK = 8;
private static final Set<UUID> UNDERSIDE_APPLIED_EFFECT = new HashSet<>();
private static final int ENTER_UNDERSIDE_Y = GRAVITY_FLIP_Y - 4;
private static final int EXIT_UNDERSIDE_Y = GRAVITY_FLIP_Y + 4;
private static final int FLIP_COOLDOWN_TICKS = 40;
private static final Set<UUID> UNDERSIDE_FORCED_GRAVITY = new HashSet<>();
private static final Set<UUID> ACTIVE_ITEMS_SCRATCH = new HashSet<>();
private static final Map<UUID, Long> LAST_FLIP_TICK = new HashMap<>();
private ThoraxisUndersideManager() {
}
@@ -39,6 +45,7 @@ public final class ThoraxisUndersideManager {
return;
}
long now = level.getGameTime();
Set<UUID> activeThisTick = ACTIVE_ITEMS_SCRATCH;
activeThisTick.clear();
int flipsRemaining = MAX_FLIPS_PER_TICK;
@@ -47,28 +54,11 @@ public final class ThoraxisUndersideManager {
continue;
}
boolean inUnderside = entity.getY() < GRAVITY_FLIP_Y;
if (entity instanceof LivingEntity living) {
UUID uuid = entity.getUUID();
if (inUnderside) {
boolean needsFlip = AntarchyGravityApi.getGravityDirection(living) != AntarchyGravityDirection.UP
|| !AntarchyGravityApi.isGravityForced(living);
if (needsFlip && flipsRemaining <= 0) {
continue;
}
if (needsFlip) {
flipsRemaining--;
}
applyUndersideInversion(living);
} else if (UNDERSIDE_APPLIED_EFFECT.remove(uuid)) {
if (living.hasEffect(AntarchyObjects.INVERTED_EFFECT.get())) {
living.removeEffect(AntarchyObjects.INVERTED_EFFECT.get());
}
if (AntarchyGravityApi.getGravityDirection(living) == AntarchyGravityDirection.UP
&& AntarchyGravityApi.isGravityForced(living)) {
AntarchyGravityApi.setGravityDirection(living, AntarchyGravityDirection.DOWN, false, TRANSITION);
}
boolean hasEffect = living.hasEffect(AntarchyObjects.INVERTED_EFFECT.get());
int threshold = hasEffect ? EXIT_UNDERSIDE_Y : ENTER_UNDERSIDE_Y;
if (living.getY() < threshold) {
refreshInvertedEffect(living);
}
continue;
}
@@ -78,40 +68,58 @@ public final class ThoraxisUndersideManager {
}
UUID uuid = entity.getUUID();
if (inUnderside) {
boolean tracked = UNDERSIDE_FORCED_GRAVITY.contains(uuid);
boolean shouldInvert = tracked ? entity.getY() < EXIT_UNDERSIDE_Y : entity.getY() < ENTER_UNDERSIDE_Y;
if (shouldInvert) {
boolean needsFlip = AntarchyGravityApi.getGravityDirection(entity) != AntarchyGravityDirection.UP
|| !AntarchyGravityApi.isGravityForced(entity);
if (needsFlip && flipsRemaining <= 0) {
continue;
}
activeThisTick.add(uuid);
UNDERSIDE_FORCED_GRAVITY.add(uuid);
if (needsFlip && (flipsRemaining <= 0 || !flipReady(uuid, now))) {
continue;
}
if (needsFlip) {
flipsRemaining--;
LAST_FLIP_TICK.put(uuid, now);
AntarchyGravityApi.setForcedGravityDirection(entity, AntarchyGravityDirection.UP, TRANSITION);
}
continue;
}
if (UNDERSIDE_FORCED_GRAVITY.remove(uuid)
&& AntarchyGravityApi.getGravityDirection(entity) == AntarchyGravityDirection.UP
&& AntarchyGravityApi.isGravityForced(entity)) {
AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION);
if (tracked) {
boolean isForcedUp = AntarchyGravityApi.getGravityDirection(entity) == AntarchyGravityDirection.UP
&& AntarchyGravityApi.isGravityForced(entity);
if (isForcedUp && !flipReady(uuid, now)) {
activeThisTick.add(uuid);
continue;
}
UNDERSIDE_FORCED_GRAVITY.remove(uuid);
if (isForcedUp) {
LAST_FLIP_TICK.put(uuid, now);
AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION);
}
}
}
UNDERSIDE_FORCED_GRAVITY.retainAll(activeThisTick);
LAST_FLIP_TICK.values().removeIf(t -> now - t > FLIP_COOLDOWN_TICKS * 4L);
}
private static boolean flipReady(UUID uuid, long now) {
Long last = LAST_FLIP_TICK.get(uuid);
return last == null || now - last >= FLIP_COOLDOWN_TICKS;
}
public static void applyUndersideInversion(LivingEntity living) {
UNDERSIDE_APPLIED_EFFECT.add(living.getUUID());
if (!living.hasEffect(AntarchyObjects.INVERTED_EFFECT.get())) {
refreshInvertedEffect(living);
}
private static void refreshInvertedEffect(LivingEntity living) {
MobEffectInstance current = living.getEffect(AntarchyObjects.INVERTED_EFFECT.get());
if (current == null || current.getDuration() <= EFFECT_REFRESH_THRESHOLD_TICKS) {
living.addEffect(new MobEffectInstance(AntarchyObjects.INVERTED_EFFECT.get(), EFFECT_DURATION_TICKS, 0, true, false, false));
}
if (AntarchyGravityApi.getGravityDirection(living) != AntarchyGravityDirection.UP
|| !AntarchyGravityApi.isGravityForced(living)) {
AntarchyGravityApi.setForcedGravityDirection(living, AntarchyGravityDirection.UP, TRANSITION);
}
}
public static boolean shouldSpawnInvertedOnDreamSand(ServerLevelAccessor level, BlockPos pos) {
@@ -159,12 +159,9 @@ public class TyphoniteSpikeFeature extends Feature<TyphoniteSpikeConfiguration>
return DripstoneThickness.TIP;
}
if (offsetFromRoot == length - 2) {
return DripstoneThickness.FRUSTUM;
return DripstoneThickness.MIDDLE;
}
if (offsetFromRoot == 0) {
return DripstoneThickness.BASE;
}
return DripstoneThickness.MIDDLE;
return DripstoneThickness.BASE;
}
private int getSpikeHeight(RandomSource random, int dx, int dz, double chance, int maxHeight, TyphoniteSpikeConfiguration config) {
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.block.DreamSandBlock;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.movement.DreamSandLowGravityAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
@@ -85,15 +84,14 @@ public abstract class DreamSandLowGravityMixin implements DreamSandLowGravityAcc
return;
}
AntarchyGravityDirection gravityDirection = AntarchyGravityApi.getGravityDirection(self);
Vec3 localMotion = AntarchyGravityRotationUtil.vecWorldToPlayer(self.getDeltaMovement(), gravityDirection);
if (localMotion.y >= 0.0D) {
Vec3 motion = self.getDeltaMovement();
boolean movingWithGravity = motion.y < 0.0D;
if (!movingWithGravity) {
return;
}
double gravityMultiplier = Math.max(0.0D, Math.min(1.0D, AntarchySettings.dreamSandGravityMultiplier()));
Vec3 adjustedLocalMotion = new Vec3(localMotion.x, localMotion.y * gravityMultiplier, localMotion.z);
self.setDeltaMovement(AntarchyGravityRotationUtil.vecPlayerToWorld(adjustedLocalMotion, gravityDirection));
self.setDeltaMovement(motion.x, motion.y * gravityMultiplier, motion.z);
self.fallDistance = 0.0F;
this.antarchy$dreamSandLandingGraceTicks = 5;
}
@@ -105,18 +103,13 @@ public abstract class DreamSandLowGravityMixin implements DreamSandLowGravityAcc
}
LivingEntity self = (LivingEntity) (Object) this;
AntarchyGravityDirection gravityDirection = AntarchyGravityApi.getGravityDirection(self);
Vec3 localMotion = AntarchyGravityRotationUtil.vecWorldToPlayer(self.getDeltaMovement(), gravityDirection);
if (localMotion.y <= 0.0D) {
Vec3 motion = self.getDeltaMovement();
boolean movingAwayFromGravity = motion.y > 0.0D;
if (!movingAwayFromGravity) {
return;
}
Vec3 adjustedLocalMotion = new Vec3(
localMotion.x,
localMotion.y * AntarchySettings.dreamSandJumpVelocityMultiplier(),
localMotion.z
);
self.setDeltaMovement(AntarchyGravityRotationUtil.vecPlayerToWorld(adjustedLocalMotion, gravityDirection));
self.setDeltaMovement(motion.x, motion.y * AntarchySettings.dreamSandJumpVelocityMultiplier(), motion.z);
self.fallDistance = 0.0F;
this.antarchy$dreamSandLandingGraceTicks = 5;
}