kings tree implementation
tons of 1.20 files
This commit is contained in:
1 parent
f4f4e0c717
commit
326c503ddb
3734 files changed
+121959
-8
No files matched your search
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package com.craisinlord.antarchy.compat.item;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
/** NBT-backed stand-in for the 1.20.1 port of Minecraft's 1.20.5+ item components. */
|
||||||
|
public final class AntarchyItemComponent<T> {
|
||||||
|
private final String key;
|
||||||
|
private final Function<CompoundTag, T> reader;
|
||||||
|
private final Function<T, Object> writer;
|
||||||
|
|
||||||
|
public AntarchyItemComponent(String key, Function<CompoundTag, T> reader, Function<T, Object> writer) {
|
||||||
|
this.key = key;
|
||||||
|
this.reader = reader;
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(ItemStack stack) {
|
||||||
|
return stack.hasTag() && stack.getTag().contains(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(ItemStack stack) {
|
||||||
|
return this.reader.apply(stack.getTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getOrDefault(ItemStack stack, T fallback) {
|
||||||
|
return this.has(stack) ? this.get(stack) : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(ItemStack stack, T value) {
|
||||||
|
Object encoded = this.writer.apply(value);
|
||||||
|
CompoundTag tag = stack.getOrCreateTag();
|
||||||
|
if (encoded instanceof Integer integer) tag.putInt(this.key, integer);
|
||||||
|
else if (encoded instanceof String string) tag.putString(this.key, string);
|
||||||
|
else if (encoded instanceof Boolean bool) tag.putBoolean(this.key, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(ItemStack stack) {
|
||||||
|
if (stack.hasTag()) stack.getTag().remove(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> AntarchyItemComponent<T> string(String key, Function<String, T> decoder, Function<T, String> encoder) {
|
||||||
|
return new AntarchyItemComponent<>(key, tag -> decoder.apply(tag.getString(key)), encoder::apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AntarchyItemComponent<Integer> integer(String key) {
|
||||||
|
return new AntarchyItemComponent<>(key, tag -> tag.getInt(key), value -> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> AntarchyItemComponent<T> marker(String key) {
|
||||||
|
return new AntarchyItemComponent<>(key, tag -> null, value -> true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,6 +103,8 @@ public final class AntarchyObjects {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND;
|
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND;
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> ROYAL_ASSAILANT_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>>) UNBOUND;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public static Supplier<Block> VORTEX_LENS = (Supplier<Block>) UNBOUND;
|
public static Supplier<Block> VORTEX_LENS = (Supplier<Block>) UNBOUND;
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static Supplier<BlockEntityType<VortexLensBlockEntity>> VORTEX_LENS_BLOCK_ENTITY = (Supplier<BlockEntityType<VortexLensBlockEntity>>) UNBOUND;
|
public static Supplier<BlockEntityType<VortexLensBlockEntity>> VORTEX_LENS_BLOCK_ENTITY = (Supplier<BlockEntityType<VortexLensBlockEntity>>) UNBOUND;
|
||||||
@@ -324,6 +326,8 @@ public final class AntarchyObjects {
|
|||||||
public static Supplier<MobEffect> GLIMMERING_EFFECT = (Supplier<MobEffect>) UNBOUND;
|
public static Supplier<MobEffect> GLIMMERING_EFFECT = (Supplier<MobEffect>) UNBOUND;
|
||||||
public static Supplier<MobEffect> DILATED_EFFECT = (Supplier<MobEffect>) UNBOUND;
|
public static Supplier<MobEffect> DILATED_EFFECT = (Supplier<MobEffect>) UNBOUND;
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Supplier<MobEffect> CONTRACTED_EFFECT = (Supplier<MobEffect>) UNBOUND;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public static Supplier<Fluid> LUMEN = (Supplier<Fluid>) UNBOUND;
|
public static Supplier<Fluid> LUMEN = (Supplier<Fluid>) UNBOUND;
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static Supplier<Fluid> FLOWING_LUMEN = (Supplier<Fluid>) UNBOUND;
|
public static Supplier<Fluid> FLOWING_LUMEN = (Supplier<Fluid>) UNBOUND;
|
||||||
@@ -699,6 +703,10 @@ public final class AntarchyObjects {
|
|||||||
ROYAL_BLACK_HOLE = supplier;
|
ROYAL_BLACK_HOLE = supplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setRoyalAssailantBlackHole(Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity>> supplier) {
|
||||||
|
ROYAL_ASSAILANT_BLACK_HOLE = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setVortexLens(Supplier<Block> supplier) {
|
public static void setVortexLens(Supplier<Block> supplier) {
|
||||||
VORTEX_LENS = supplier;
|
VORTEX_LENS = supplier;
|
||||||
}
|
}
|
||||||
@@ -819,6 +827,10 @@ public final class AntarchyObjects {
|
|||||||
DILATED_EFFECT = supplier;
|
DILATED_EFFECT = supplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setContractedEffect(Supplier<MobEffect> supplier) {
|
||||||
|
CONTRACTED_EFFECT = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setElka(Supplier<EntityType<com.craisinlord.antarchy.content.entity.ElkaEntity>> supplier) {
|
public static void setElka(Supplier<EntityType<com.craisinlord.antarchy.content.entity.ElkaEntity>> supplier) {
|
||||||
ELKA = supplier;
|
ELKA = supplier;
|
||||||
}
|
}
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.block.entity.DimensionalTearMarkerBlockEntity;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
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.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
|
||||||
|
public class DimensionalTearMarkerBlock extends BaseEntityBlock {
|
||||||
|
private static final VoxelShape EMPTY = Shapes.empty();
|
||||||
|
private final Supplier<? extends BlockEntityType<DimensionalTearMarkerBlockEntity>> blockEntityTypeSupplier;
|
||||||
|
|
||||||
|
public DimensionalTearMarkerBlock(Supplier<? extends BlockEntityType<DimensionalTearMarkerBlockEntity>> blockEntityTypeSupplier, BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.blockEntityTypeSupplier = blockEntityTypeSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderShape getRenderShape(BlockState state) {
|
||||||
|
return RenderShape.INVISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new DimensionalTearMarkerBlockEntity(pos, state, this.blockEntityTypeSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
|
||||||
|
if (level.isClientSide) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createTickerHelper(blockEntityType, this.blockEntityTypeSupplier.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
|
||||||
|
if (tickLevel instanceof ServerLevel serverLevel) {
|
||||||
|
DimensionalTearMarkerBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
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 DirectionProperty VERTICAL_DIRECTION = BlockStateProperties.VERTICAL_DIRECTION;
|
||||||
|
private static final ResourceLocation NYXITE_ID = new ResourceLocation(Antarchy.MODID, "nyxite");
|
||||||
|
private static final ResourceLocation NADIR_FERN_ID = new ResourceLocation(Antarchy.MODID, "nadir_fern");
|
||||||
|
private static final ResourceLocation DUSKBELL_ID = new ResourceLocation(Antarchy.MODID, "duskbell");
|
||||||
|
private static final ResourceLocation NADIR_SAPLING_ID = new ResourceLocation(Antarchy.MODID, "nadir_sapling");
|
||||||
|
private static final ResourceLocation WHIRLFLOWER_ID = new ResourceLocation(Antarchy.MODID, "whirlflower");
|
||||||
|
private static final ResourceLocation SPIRALING_VINES_ID = new ResourceLocation(Antarchy.MODID, "spiraling_vines");
|
||||||
|
private static final ResourceLocation NADIR_ROOTS_ID = new ResourceLocation(Antarchy.MODID, "nadir_roots");
|
||||||
|
private static final ResourceLocation VERDANT_ROOTS_ID = new ResourceLocation(Antarchy.MODID, "verdant_roots");
|
||||||
|
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
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
return this.defaultBlockState().setValue(
|
||||||
|
VERTICAL_DIRECTION,
|
||||||
|
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(VERTICAL_DIRECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, boolean isClientSide) {
|
||||||
|
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 switch (random.nextInt(5)) {
|
||||||
|
case 0 -> DUSKBELL_ID;
|
||||||
|
case 1, 2 -> NADIR_ROOTS_ID;
|
||||||
|
default -> NADIR_FERN_ID;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case VERDANT -> switch (random.nextInt(5)) {
|
||||||
|
case 0, 1 -> VERDANT_ROOTS_ID;
|
||||||
|
case 2 -> SPIRALING_VINES_ID;
|
||||||
|
default -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||||
|
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 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
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
return this.defaultBlockState().setValue(
|
||||||
|
VERTICAL_DIRECTION,
|
||||||
|
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return state.getValue(VERTICAL_DIRECTION) == Direction.DOWN ? DOWN_SHAPE : UP_SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
|
return state.is(AntarchyTags.Blocks.NYXIUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||||
|
Direction growthDirection = state.getValue(VERTICAL_DIRECTION);
|
||||||
|
BlockPos supportPos = pos.relative(growthDirection.getOpposite());
|
||||||
|
return this.mayPlaceOn(level.getBlockState(supportPos), level, supportPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(VERTICAL_DIRECTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
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 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 BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
return this.defaultBlockState().setValue(
|
||||||
|
VERTICAL_DIRECTION,
|
||||||
|
context.getClickedFace() == Direction.DOWN ? Direction.DOWN : Direction.UP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(VERTICAL_DIRECTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
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.DoublePlantBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
|
||||||
|
/** A two-block-tall, light-emitting reed found around Lumen pools. */
|
||||||
|
public final class GlimmeringReedBlock extends DoublePlantBlock implements BonemealableBlock {
|
||||||
|
|
||||||
|
public GlimmeringReedBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult use(
|
||||||
|
BlockState state,
|
||||||
|
Level level,
|
||||||
|
BlockPos pos,
|
||||||
|
Player player,
|
||||||
|
InteractionHand hand,
|
||||||
|
BlockHitResult hitResult
|
||||||
|
) {
|
||||||
|
if (player.getItemInHand(hand).is(Items.BONE_MEAL)) {
|
||||||
|
popResource(level, pos, new ItemStack(state.getBlock().asItem(), 1));
|
||||||
|
return InteractionResult.PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.use(state, level, pos, player, hand, hitResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, boolean isClientSide) {
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
|
||||||
|
super.animateTick(state, level, pos, random);
|
||||||
|
if (state.getValue(HALF) != DoubleBlockHalf.UPPER || random.nextInt(8) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double x = pos.getX() + 0.15D + random.nextDouble() * 0.7D;
|
||||||
|
double y = pos.getY() + 0.76D + random.nextDouble() * 0.18D;
|
||||||
|
double z = pos.getZ() + 0.15D + random.nextDouble() * 0.7D;
|
||||||
|
level.addParticle(ParticleTypes.GLOW, x, y, z, 0.0D, 0.02D, 0.0D);
|
||||||
|
|
||||||
|
if (random.nextInt(4) == 0) {
|
||||||
|
level.addParticle(ParticleTypes.ENCHANT, x, y + 0.02D, z, 0.0D, 0.015D, 0.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.BushBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
|
||||||
|
public class GorevineBlock extends SimpleDirectionalVineBlock {
|
||||||
|
private static final int MAX_GROWTH_DISTANCE = 7;
|
||||||
|
|
||||||
|
public GorevineBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int maxDistance() {
|
||||||
|
return MAX_GROWTH_DISTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
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 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
|
||||||
|
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
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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
|
||||||
|
public 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
|
||||||
|
public 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
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
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 {
|
||||||
|
private static final ResourceLocation LARGE_NADIR_FERN_ID = new ResourceLocation(Antarchy.MODID, "large_nadir_fern");
|
||||||
|
|
||||||
|
public NadirFernBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, boolean isClientSide) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.particles.DustParticleOptions;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class NadirVeilBlock extends Block {
|
||||||
|
|
||||||
|
private static final int GLOW_LIGHT_LEVEL = 6;
|
||||||
|
private static final DustParticleOptions DRIP_PARTICLE =
|
||||||
|
new DustParticleOptions(new Vector3f(0.63F, 0.26F, 0.92F), 1.0F);
|
||||||
|
|
||||||
|
public NadirVeilBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties.lightLevel(state -> GLOW_LIGHT_LEVEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
|
||||||
|
if (random.nextInt(6) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double x = pos.getX() + 0.5D + (random.nextDouble() - 0.5D) * 0.7D;
|
||||||
|
double y = pos.getY() + 0.15D + random.nextDouble() * 0.4D;
|
||||||
|
double z = pos.getZ() + 0.5D + (random.nextDouble() - 0.5D) * 0.7D;
|
||||||
|
level.addParticle(DRIP_PARTICLE, x, y, z, 0.0D, 0.02D + random.nextDouble() * 0.02D, 0.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
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.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
|
||||||
|
public class PortalGunPortalBaseBlock extends BaseEntityBlock {
|
||||||
|
private static final VoxelShape EMPTY = Shapes.empty();
|
||||||
|
private final Supplier<? extends BlockEntityType<PortalGunPortalBaseBlockEntity>> blockEntityTypeSupplier;
|
||||||
|
|
||||||
|
public PortalGunPortalBaseBlock(Supplier<? extends BlockEntityType<PortalGunPortalBaseBlockEntity>> blockEntityTypeSupplier, BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.blockEntityTypeSupplier = blockEntityTypeSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderShape getRenderShape(BlockState state) {
|
||||||
|
return RenderShape.INVISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new PortalGunPortalBaseBlockEntity(pos, state, this.blockEntityTypeSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
|
||||||
|
if (level.isClientSide) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createTickerHelper(blockEntityType, this.blockEntityTypeSupplier.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
|
||||||
|
if (tickLevel instanceof net.minecraft.server.level.ServerLevel serverLevel) {
|
||||||
|
PortalGunPortalBaseBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
|
if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof PortalGunPortalBaseBlockEntity blockEntity) {
|
||||||
|
blockEntity.onBroken();
|
||||||
|
}
|
||||||
|
super.onRemove(state, level, pos, newState, movedByPiston);
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
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.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
|
||||||
|
public class PortalGunPortalMasterBlock extends BaseEntityBlock {
|
||||||
|
private static final VoxelShape EMPTY = Shapes.empty();
|
||||||
|
private final Supplier<? extends BlockEntityType<PortalGunPortalMasterBlockEntity>> blockEntityTypeSupplier;
|
||||||
|
|
||||||
|
public PortalGunPortalMasterBlock(Supplier<? extends BlockEntityType<PortalGunPortalMasterBlockEntity>> blockEntityTypeSupplier, BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.blockEntityTypeSupplier = blockEntityTypeSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderShape getRenderShape(BlockState state) {
|
||||||
|
return RenderShape.INVISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new PortalGunPortalMasterBlockEntity(pos, state, this.blockEntityTypeSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
|
||||||
|
if (level.isClientSide) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createTickerHelper(blockEntityType, this.blockEntityTypeSupplier.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
|
||||||
|
if (tickLevel instanceof net.minecraft.server.level.ServerLevel serverLevel) {
|
||||||
|
PortalGunPortalMasterBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
|
if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof PortalGunPortalMasterBlockEntity blockEntity) {
|
||||||
|
blockEntity.onBroken();
|
||||||
|
}
|
||||||
|
super.onRemove(state, level, pos, newState, movedByPiston);
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
|
||||||
|
public class PrinceEggBlock extends RoyalEggBlock {
|
||||||
|
|
||||||
|
public PrinceEggBlock(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityType<? extends RoyalMountEntity> mountType() {
|
||||||
|
return AntarchyObjects.PRINCE.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
|
||||||
|
public class PrincessEggBlock extends RoyalEggBlock {
|
||||||
|
|
||||||
|
public PrincessEggBlock(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityType<? extends RoyalMountEntity> mountType() {
|
||||||
|
return AntarchyObjects.PRINCESS.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.block.entity.RoyalEggBlockEntity;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.MobSpawnType;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.FallingBlock;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
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.IntegerProperty;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public abstract class RoyalEggBlock extends BaseEntityBlock {
|
||||||
|
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
|
||||||
|
private static final int MAX_HATCH = 2;
|
||||||
|
private static final int FALL_DELAY = 2;
|
||||||
|
|
||||||
|
private static final VoxelShape SHAPE = Shapes.or(
|
||||||
|
Block.box(3.0D, 0.0D, 3.0D, 13.0D, 13.0D, 13.0D),
|
||||||
|
Block.box(2.0D, 1.0D, 2.0D, 14.0D, 11.0D, 14.0D),
|
||||||
|
Block.box(4.0D, 13.0D, 4.0D, 12.0D, 14.0D, 12.0D),
|
||||||
|
Block.box(5.0D, 14.0D, 5.0D, 11.0D, 15.0D, 11.0D),
|
||||||
|
Block.box(6.0D, 15.0D, 6.0D, 10.0D, 16.0D, 10.0D));
|
||||||
|
|
||||||
|
public RoyalEggBlock(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract EntityType<? extends RoyalMountEntity> mountType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(HATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderShape getRenderShape(BlockState state) {
|
||||||
|
return RenderShape.MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new RoyalEggBlockEntity(pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||||
|
super.setPlacedBy(level, pos, state, placer, stack);
|
||||||
|
if (!level.isClientSide && placer instanceof Player player
|
||||||
|
&& level.getBlockEntity(pos) instanceof RoyalEggBlockEntity egg) {
|
||||||
|
egg.setPlacerUuid(player.getUUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attack(BlockState state, Level level, BlockPos pos, Player player) {
|
||||||
|
if (level instanceof ServerLevel serverLevel) {
|
||||||
|
this.teleport(state, serverLevel, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void teleport(BlockState state, ServerLevel level, BlockPos pos) {
|
||||||
|
RandomSource random = level.random;
|
||||||
|
for (int attempt = 0; attempt < 32; attempt++) {
|
||||||
|
BlockPos target = pos.offset(
|
||||||
|
random.nextInt(16) - random.nextInt(16),
|
||||||
|
random.nextInt(8) - random.nextInt(8),
|
||||||
|
random.nextInt(16) - random.nextInt(16));
|
||||||
|
if (level.getBlockState(target).isAir() && target.getY() > level.getMinBuildHeight()) {
|
||||||
|
RoyalEggBlockEntity source = level.getBlockEntity(pos) instanceof RoyalEggBlockEntity be ? be : null;
|
||||||
|
UUID placer = source == null ? null : source.getPlacerUuid();
|
||||||
|
int hatch = state.getValue(HATCH);
|
||||||
|
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 2);
|
||||||
|
level.setBlock(target, state.setValue(HATCH, Math.max(0, hatch - 1)), 2);
|
||||||
|
if (level.getBlockEntity(target) instanceof RoyalEggBlockEntity moved) {
|
||||||
|
moved.setPlacerUuid(placer);
|
||||||
|
}
|
||||||
|
level.levelEvent(2003, pos, 0);
|
||||||
|
level.playSound(null, pos, SoundEvents.ENDER_DRAGON_FLAP, SoundSource.BLOCKS, 0.6F, 1.4F);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
|
||||||
|
level.scheduleTick(pos, this, FALL_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||||
|
level.scheduleTick(pos, this, FALL_DELAY);
|
||||||
|
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
if (!FallingBlock.isFree(level.getBlockState(pos.below())) || pos.getY() < level.getMinBuildHeight()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CompoundTag blockData = level.getBlockEntity(pos) instanceof RoyalEggBlockEntity egg
|
||||||
|
? egg.saveWithoutMetadata()
|
||||||
|
: null;
|
||||||
|
FallingBlockEntity falling = FallingBlockEntity.fall(level, pos, state);
|
||||||
|
if (blockData != null && !blockData.isEmpty()) {
|
||||||
|
falling.blockData = blockData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRandomlyTicking(BlockState state) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
if (random.nextInt(Math.max(1, AntarchySettings.royalEggHatchChance())) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int hatch = state.getValue(HATCH);
|
||||||
|
if (hatch < MAX_HATCH) {
|
||||||
|
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
|
||||||
|
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hatch(level, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hatch(ServerLevel level, BlockPos pos) {
|
||||||
|
UUID placer = level.getBlockEntity(pos) instanceof RoyalEggBlockEntity be ? be.getPlacerUuid() : null;
|
||||||
|
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.9F, 1.0F);
|
||||||
|
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
||||||
|
|
||||||
|
RoyalMountEntity mount = this.mountType().create(level);
|
||||||
|
if (mount == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mount.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, level.random.nextFloat() * 360.0F, 0.0F);
|
||||||
|
mount.setBaby(true);
|
||||||
|
mount.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.BREEDING, null, null);
|
||||||
|
if (placer != null) {
|
||||||
|
mount.setTame(true);
|
||||||
|
mount.setOwnerUUID(placer);
|
||||||
|
if (level.getPlayerByUUID(placer) instanceof ServerPlayer owner) {
|
||||||
|
mount.tame(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
level.addFreshEntity(mount);
|
||||||
|
level.sendParticles(ParticleTypes.HAPPY_VILLAGER, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 24, 0.4D, 0.4D, 0.4D, 0.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
+139
@@ -0,0 +1,139 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.tags.BlockTags;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
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.LeavesBlock;
|
||||||
|
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.IntegerProperty;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
|
||||||
|
public class RoyalLeavesBlock extends LeavesBlock {
|
||||||
|
public static final IntegerProperty ROYAL_DISTANCE = IntegerProperty.create("royal_distance", 1, 17);
|
||||||
|
public static final BooleanProperty PERSISTENT = LeavesBlock.PERSISTENT;
|
||||||
|
public static final BooleanProperty WATERLOGGED = LeavesBlock.WATERLOGGED;
|
||||||
|
public static final int ROYAL_MAX_DISTANCE = 16;
|
||||||
|
public static final int ROYAL_DECAY_DISTANCE = ROYAL_MAX_DISTANCE + 1;
|
||||||
|
|
||||||
|
public RoyalLeavesBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.registerDefaultState(this.stateDefinition.any()
|
||||||
|
.setValue(DISTANCE, DECAY_DISTANCE)
|
||||||
|
.setValue(ROYAL_DISTANCE, ROYAL_DECAY_DISTANCE)
|
||||||
|
.setValue(PERSISTENT, false)
|
||||||
|
.setValue(WATERLOGGED, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
FluidState fluidState = context.getLevel().getFluidState(context.getClickedPos());
|
||||||
|
BlockState state = this.defaultBlockState()
|
||||||
|
.setValue(PERSISTENT, true)
|
||||||
|
.setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER);
|
||||||
|
|
||||||
|
return recalculateDistance(state, context.getLevel(), context.getClickedPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(LeavesBlock.DISTANCE, ROYAL_DISTANCE, PERSISTENT, WATERLOGGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRandomlyTicking(BlockState state) {
|
||||||
|
return decaying(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean decaying(BlockState state) {
|
||||||
|
return !state.getValue(PERSISTENT) && state.getValue(ROYAL_DISTANCE) > ROYAL_MAX_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
if (decaying(state)) {
|
||||||
|
dropResources(state, level, pos);
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
level.setBlock(pos, recalculateDistance(state, level, pos), Block.UPDATE_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState updateShape(
|
||||||
|
BlockState state,
|
||||||
|
Direction direction,
|
||||||
|
BlockState neighborState,
|
||||||
|
LevelAccessor level,
|
||||||
|
BlockPos pos,
|
||||||
|
BlockPos neighborPos
|
||||||
|
) {
|
||||||
|
if (state.getValue(WATERLOGGED)) {
|
||||||
|
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
|
||||||
|
}
|
||||||
|
|
||||||
|
int neighborDistance = Math.min(ROYAL_DECAY_DISTANCE, getDistanceAt(neighborState) + 1);
|
||||||
|
int currentDistance = state.getValue(ROYAL_DISTANCE);
|
||||||
|
|
||||||
|
if (neighborDistance != 1 || currentDistance != neighborDistance) {
|
||||||
|
level.scheduleTick(pos, this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockState recalculateDistance(BlockState state, LevelReader level, BlockPos pos) {
|
||||||
|
int bestDistance = ROYAL_DECAY_DISTANCE;
|
||||||
|
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
|
||||||
|
|
||||||
|
for (Direction direction : Direction.values()) {
|
||||||
|
cursor.setWithOffset(pos, direction);
|
||||||
|
bestDistance = Math.min(bestDistance, getDistanceAt(level.getBlockState(cursor)) + 1);
|
||||||
|
if (bestDistance == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setRoyalDistance(state, Math.min(ROYAL_DECAY_DISTANCE, bestDistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockState setRoyalDistance(BlockState state, int distance) {
|
||||||
|
int royalDistance = Math.max(1, Math.min(ROYAL_DECAY_DISTANCE, distance));
|
||||||
|
int vanillaDistance = Math.min(LeavesBlock.DECAY_DISTANCE, royalDistance);
|
||||||
|
|
||||||
|
return state
|
||||||
|
.setValue(ROYAL_DISTANCE, royalDistance)
|
||||||
|
.setValue(LeavesBlock.DISTANCE, vanillaDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getDistanceAt(BlockState state) {
|
||||||
|
if (state.is(BlockTags.LOGS)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (state.hasProperty(ROYAL_DISTANCE)) {
|
||||||
|
return state.getValue(ROYAL_DISTANCE);
|
||||||
|
}
|
||||||
|
if (state.hasProperty(LeavesBlock.DISTANCE)) {
|
||||||
|
return state.getValue(LeavesBlock.DISTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ROYAL_DECAY_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockState setRoyalDistanceForWorldgen(BlockState state, int distance) {
|
||||||
|
return setRoyalDistance(state, distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.advancement.AntarchyAdvancements;
|
||||||
|
import com.craisinlord.antarchy.content.worldgen.royal.RoyalTreeGrowers;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.tags.BlockTags;
|
||||||
|
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.levelgen.feature.ConfiguredFeature;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
|
||||||
|
public class RoyalSaplingBlock extends BushBlock implements BonemealableBlock {
|
||||||
|
private static final ResourceLocation LET_IT_GROW_ADVANCEMENT = new ResourceLocation(Antarchy.MODID, "let_it_grow");
|
||||||
|
private static final VoxelShape SHAPE = Block.box(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D);
|
||||||
|
|
||||||
|
public RoyalSaplingBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
|
return state.is(BlockTags.DIRT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||||
|
return level.getBlockState(pos.below()).is(BlockTags.DIRT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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
|
||||||
|
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
if (level.getMaxLocalRawBrightness(pos.above()) >= 9 && random.nextInt(7) == 0) {
|
||||||
|
this.growRoyalTree(level, pos, random);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, boolean isClientSide) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
|
||||||
|
return random.nextFloat() < 0.45F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
|
||||||
|
this.growRoyalTree(level, pos, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void growRoyalTree(ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
Map<BlockPos, BlockState> footprint = this.findFootprint(level, pos);
|
||||||
|
if (footprint == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos origin = footprint.keySet().iterator().next();
|
||||||
|
ResourceKey<ConfiguredFeature<?, ?>> variant = switch (random.nextInt(3)) {
|
||||||
|
case 0 -> RoyalTreeGrowers.ROYAL_TREE_SMALL;
|
||||||
|
case 1 -> RoyalTreeGrowers.ROYAL_TREE_MEDIUM;
|
||||||
|
default -> RoyalTreeGrowers.ROYAL_TREE_LARGE;
|
||||||
|
};
|
||||||
|
|
||||||
|
Holder.Reference<ConfiguredFeature<?, ?>> feature = level.registryAccess()
|
||||||
|
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
|
||||||
|
.getOrThrow(variant);
|
||||||
|
|
||||||
|
for (BlockPos saplingPos : footprint.keySet()) {
|
||||||
|
level.removeBlock(saplingPos, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature.value().place(level, level.getChunkSource().getGenerator(), random, origin)) {
|
||||||
|
for (ServerPlayer player : level.getEntitiesOfClass(ServerPlayer.class, new AABB(origin).inflate(8.0D))) {
|
||||||
|
AntarchyAdvancements.award(player, LET_IT_GROW_ADVANCEMENT);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<BlockPos, BlockState> entry : footprint.entrySet()) {
|
||||||
|
level.setBlock(entry.getKey(), entry.getValue(), Block.UPDATE_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<BlockPos, BlockState> findFootprint(ServerLevel level, BlockPos pos) {
|
||||||
|
for (int offsetX = 0; offsetX < 2; offsetX++) {
|
||||||
|
for (int offsetZ = 0; offsetZ < 2; offsetZ++) {
|
||||||
|
BlockPos origin = pos.offset(-offsetX, 0, -offsetZ);
|
||||||
|
Map<BlockPos, BlockState> blocks = new LinkedHashMap<>();
|
||||||
|
boolean matches = true;
|
||||||
|
|
||||||
|
for (int x = 0; x < 2 && matches; x++) {
|
||||||
|
for (int z = 0; z < 2; z++) {
|
||||||
|
BlockPos checkPos = origin.offset(x, 0, z);
|
||||||
|
BlockState checkState = level.getBlockState(checkPos);
|
||||||
|
if (!checkState.is(this)) {
|
||||||
|
matches = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
blocks.put(checkPos, checkState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
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 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, 15);
|
||||||
|
public static final int MAX_DISTANCE = 6;
|
||||||
|
public 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 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
|
||||||
|
public 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
|
||||||
|
public 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
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int maxDistance() {
|
||||||
|
return MAX_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRandomlyTicking(BlockState state) {
|
||||||
|
return isTip(state) && state.getValue(DISTANCE) < maxDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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, boolean isClientSide) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nextDistance = Math.min(state.getValue(DISTANCE) + 1, maxDistance());
|
||||||
|
BlockState grownState = this.defaultBlockState()
|
||||||
|
.setValue(GROWTH_DIRECTION, direction)
|
||||||
|
.setValue(DISTANCE, nextDistance);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isTip(BlockState state) {
|
||||||
|
return state.getValue(GROWTH_DIRECTION) == Direction.DOWN ? state.getValue(BOTTOM_CAP) : state.getValue(TOP_CAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(GROWTH_DIRECTION, TOP_CAP, BOTTOM_CAP, DISTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.BushBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
|
||||||
|
public class SpiralingVinesBlock extends SimpleDirectionalVineBlock {
|
||||||
|
|
||||||
|
public SpiralingVinesBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||||
|
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 TruffaloLogBlock extends RotatedPillarBlock {
|
||||||
|
|
||||||
|
private static final VoxelShape SHAPE_Y = Block.box(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D);
|
||||||
|
private static final VoxelShape SHAPE_X = Block.box(0.0D, 4.0D, 4.0D, 16.0D, 12.0D, 12.0D);
|
||||||
|
private static final VoxelShape SHAPE_Z = Block.box(4.0D, 4.0D, 0.0D, 12.0D, 12.0D, 16.0D);
|
||||||
|
|
||||||
|
public TruffaloLogBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return shapeFor(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return shapeFor(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getOcclusionShape(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
|
return shapeFor(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VoxelShape shapeFor(BlockState state) {
|
||||||
|
return switch (state.getValue(AXIS)) {
|
||||||
|
case X -> SHAPE_X;
|
||||||
|
case Z -> SHAPE_Z;
|
||||||
|
default -> SHAPE_Y;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.advancement.AntarchyAdvancements;
|
||||||
|
import com.craisinlord.antarchy.content.worldgen.truffalo.TruffaloTreeGrowers;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
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.levelgen.feature.ConfiguredFeature;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
|
||||||
|
public class TruffaloSaplingBlock extends BushBlock implements BonemealableBlock {
|
||||||
|
private static final ResourceLocation LET_IT_GROW_ADVANCEMENT = new ResourceLocation(Antarchy.MODID, "let_it_grow");
|
||||||
|
private static final TagKey<Block> SUPPORTS = TagKey.create(
|
||||||
|
Registries.BLOCK,
|
||||||
|
new ResourceLocation(Antarchy.MODID, "truffalo_sapling_supports")
|
||||||
|
);
|
||||||
|
private static final VoxelShape SHAPE = Block.box(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D);
|
||||||
|
|
||||||
|
public TruffaloSaplingBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
|
return state.is(SUPPORTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||||
|
BlockPos supportPos = pos.below();
|
||||||
|
BlockState supportState = level.getBlockState(supportPos);
|
||||||
|
return supportState.is(SUPPORTS) && supportState.isFaceSturdy(level, supportPos, Direction.UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
if (random.nextInt(7) == 0) {
|
||||||
|
this.growConfiguredTree(level, pos, state, random);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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
|
||||||
|
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, boolean isClientSide) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
|
||||||
|
return random.nextFloat() < 0.45F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
|
||||||
|
this.growConfiguredTree(level, pos, state, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void growConfiguredTree(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||||
|
Holder.Reference<ConfiguredFeature<?, ?>> feature = level.registryAccess()
|
||||||
|
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
|
||||||
|
.getOrThrow(TruffaloTreeGrowers.TRUFFALO_TREE);
|
||||||
|
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
if (feature.value().place(level, level.getChunkSource().getGenerator(), random, pos)) {
|
||||||
|
for (ServerPlayer player : level.getEntitiesOfClass(ServerPlayer.class, new AABB(pos).inflate(8.0D))) {
|
||||||
|
AntarchyAdvancements.award(player, LET_IT_GROW_ADVANCEMENT);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
level.setBlock(pos, state, Block.UPDATE_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ShearsItem;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class TruffaloTuftBlock extends Block {
|
||||||
|
private static final float SHEARS_DESTROY_SPEED = 10.0F;
|
||||||
|
|
||||||
|
public TruffaloTuftBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDestroyProgress(BlockState state, Player player, BlockGetter level, BlockPos pos) {
|
||||||
|
if (player.getMainHandItem().getItem() instanceof ShearsItem) {
|
||||||
|
return SHEARS_DESTROY_SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getDestroyProgress(state, player, level, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.entity.UpwardFallingBlockEntity;
|
||||||
|
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.item.FallingBlockEntity;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
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.PointedDripstoneBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DripstoneThickness;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
|
||||||
|
public class TyphoniteSpikeBlock extends PointedDripstoneBlock {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
|
||||||
|
public TyphoniteSpikeBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
LevelAccessor level = context.getLevel();
|
||||||
|
BlockPos pos = context.getClickedPos();
|
||||||
|
Direction direction = calculateTipDirection(level, pos, context.getNearestLookingVerticalDirection().getOpposite());
|
||||||
|
if (direction == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean mergeTips = !context.isSecondaryUseActive();
|
||||||
|
DripstoneThickness thickness = calculateDripstoneThickness(level, pos, direction, mergeTips);
|
||||||
|
if (thickness == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.defaultBlockState()
|
||||||
|
.setValue(TIP_DIRECTION, direction)
|
||||||
|
.setValue(THICKNESS, thickness)
|
||||||
|
.setValue(WATERLOGGED, level.getFluidState(pos).getType() == Fluids.WATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState updateShape(
|
||||||
|
BlockState state,
|
||||||
|
Direction direction,
|
||||||
|
BlockState neighborState,
|
||||||
|
LevelAccessor level,
|
||||||
|
BlockPos pos,
|
||||||
|
BlockPos neighborPos
|
||||||
|
) {
|
||||||
|
if (state.getValue(WATERLOGGED)) {
|
||||||
|
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction != Direction.UP && direction != Direction.DOWN) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction tipDirection = state.getValue(TIP_DIRECTION);
|
||||||
|
if (tipDirection == Direction.DOWN && level.getBlockTicks().hasScheduledTick(pos, this)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == tipDirection.getOpposite() && !this.canSurvive(state, level, pos)) {
|
||||||
|
if (tipDirection == Direction.DOWN) {
|
||||||
|
level.scheduleTick(pos, this, 2);
|
||||||
|
} else {
|
||||||
|
level.scheduleTick(pos, this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean mergeTips = state.getValue(THICKNESS) == DripstoneThickness.TIP_MERGE;
|
||||||
|
return state.setValue(THICKNESS, calculateDripstoneThickness(level, pos, tipDirection, mergeTips));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||||
|
return isValidPointedDripstonePlacement(level, pos, state.getValue(TIP_DIRECTION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
if (this.canSurvive(state, level, pos)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.getValue(TIP_DIRECTION) == Direction.DOWN) {
|
||||||
|
BlockPos.MutableBlockPos root = pos.mutable();
|
||||||
|
while (isTyphoniteSpikeWithDirection(level.getBlockState(root.above()), Direction.DOWN)) {
|
||||||
|
root.move(Direction.UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos.MutableBlockPos cursor = root.mutable();
|
||||||
|
while (isTyphoniteSpikeWithDirection(level.getBlockState(cursor), Direction.DOWN)) {
|
||||||
|
BlockState fallingState = level.getBlockState(cursor);
|
||||||
|
if (isTyphoniteSpikeWithDirection(fallingState, Direction.DOWN)) {
|
||||||
|
FallingBlockEntity.fall(level, cursor, fallingState);
|
||||||
|
}
|
||||||
|
cursor.move(Direction.DOWN);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos.MutableBlockPos cursor = pos.mutable();
|
||||||
|
while (isTyphoniteSpikeWithDirection(level.getBlockState(cursor), Direction.UP)) {
|
||||||
|
BlockState spikeState = level.getBlockState(cursor);
|
||||||
|
UpwardFallingBlockEntity.fallUp(level, cursor, spikeState);
|
||||||
|
cursor.move(Direction.UP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Direction calculateTipDirection(LevelReader level, BlockPos pos, Direction preferredDirection) {
|
||||||
|
if (isValidPointedDripstonePlacement(level, pos, preferredDirection)) {
|
||||||
|
return preferredDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction opposite = preferredDirection.getOpposite();
|
||||||
|
return isValidPointedDripstonePlacement(level, pos, opposite) ? opposite : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DripstoneThickness calculateDripstoneThickness(LevelReader level, BlockPos pos, Direction direction, boolean mergeTips) {
|
||||||
|
Direction opposite = direction.getOpposite();
|
||||||
|
BlockState forwardState = level.getBlockState(pos.relative(direction));
|
||||||
|
if (isTyphoniteSpikeWithDirection(forwardState, opposite)) {
|
||||||
|
return !mergeTips && forwardState.getValue(THICKNESS) != DripstoneThickness.TIP_MERGE
|
||||||
|
? DripstoneThickness.TIP
|
||||||
|
: DripstoneThickness.TIP_MERGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTyphoniteSpikeWithDirection(forwardState, direction)) {
|
||||||
|
return DripstoneThickness.TIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
DripstoneThickness forwardThickness = forwardState.getValue(THICKNESS);
|
||||||
|
if (forwardThickness == DripstoneThickness.TIP || forwardThickness == DripstoneThickness.TIP_MERGE) {
|
||||||
|
return DripstoneThickness.MIDDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DripstoneThickness.BASE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidPointedDripstonePlacement(LevelReader level, BlockPos pos, Direction direction) {
|
||||||
|
BlockPos supportPos = pos.relative(direction.getOpposite());
|
||||||
|
BlockState supportState = level.getBlockState(supportPos);
|
||||||
|
return supportState.isFaceSturdy(level, supportPos, direction) || isTyphoniteSpikeWithDirection(supportState, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTyphoniteSpikeWithDirection(BlockState state, Direction direction) {
|
||||||
|
return state.getBlock() instanceof TyphoniteSpikeBlock && state.getValue(TIP_DIRECTION) == direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.block.entity.VortexLensBlockEntity;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
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.BooleanProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class VortexLensBlock extends BaseEntityBlock {
|
||||||
|
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||||
|
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
|
||||||
|
public static final BooleanProperty PUSHING = BooleanProperty.create("pushing");
|
||||||
|
|
||||||
|
private final Supplier<? extends BlockEntityType<VortexLensBlockEntity>> blockEntityTypeSupplier;
|
||||||
|
|
||||||
|
public VortexLensBlock(Supplier<? extends BlockEntityType<VortexLensBlockEntity>> blockEntityTypeSupplier,
|
||||||
|
BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.blockEntityTypeSupplier = blockEntityTypeSupplier;
|
||||||
|
this.registerDefaultState(this.stateDefinition.any()
|
||||||
|
.setValue(FACING, Direction.UP).setValue(POWERED, false).setValue(PUSHING, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(FACING, POWERED, PUSHING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
Level level = context.getLevel();
|
||||||
|
BlockPos pos = context.getClickedPos();
|
||||||
|
boolean bluestone = BluestoneSignalHelper.getBestNeighborSignal(level, pos) > 0;
|
||||||
|
return this.defaultBlockState()
|
||||||
|
.setValue(FACING, context.getClickedFace())
|
||||||
|
.setValue(POWERED, hasAnySignal(level, pos))
|
||||||
|
.setValue(PUSHING, bluestone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos,
|
||||||
|
boolean movedByPiston) {
|
||||||
|
if (!level.isClientSide && level.getBlockEntity(pos) instanceof VortexLensBlockEntity lens) {
|
||||||
|
lens.refreshImmediately();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderShape getRenderShape(BlockState state) {
|
||||||
|
return RenderShape.MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new VortexLensBlockEntity(pos, state, this.blockEntityTypeSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state,
|
||||||
|
BlockEntityType<T> blockEntityType) {
|
||||||
|
if (level.isClientSide) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createTickerHelper(blockEntityType, this.blockEntityTypeSupplier.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
|
||||||
|
if (tickLevel instanceof net.minecraft.server.level.ServerLevel serverLevel) {
|
||||||
|
VortexLensBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
|
if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof VortexLensBlockEntity lens) {
|
||||||
|
lens.removeActiveVortex();
|
||||||
|
}
|
||||||
|
super.onRemove(state, level, pos, newState, movedByPiston);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState rotate(BlockState state, Rotation rotation) {
|
||||||
|
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState mirror(BlockState state, Mirror mirror) {
|
||||||
|
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasAnySignal(Level level, BlockPos pos) {
|
||||||
|
return level.getBestNeighborSignal(pos) > 0 || BluestoneSignalHelper.getBestNeighborSignal(level, pos) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||||
|
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 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 = new ResourceLocation(Antarchy.MODID, "veined_typhonite");
|
||||||
|
|
||||||
|
public WhirlflowerBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.registerDefaultState(this.stateDefinition.any().setValue(VERTICAL_DIRECTION, Direction.UP));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
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
|
||||||
|
public boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
|
return state.is(AntarchyTags.Blocks.NYXIUM)
|
||||||
|
|| BuiltInRegistries.BLOCK.getKey(state.getBlock()).equals(VEINED_TYPHONITE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||||
|
Direction growthDirection = state.getValue(VERTICAL_DIRECTION);
|
||||||
|
BlockPos supportPos = pos.relative(growthDirection.getOpposite());
|
||||||
|
return this.mayPlaceOn(level.getBlockState(supportPos), level, supportPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public 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
|
||||||
|
public void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(VERTICAL_DIRECTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class DimensionalTearMarkerBlockEntity extends BlockEntity {
|
||||||
|
private static final int MAX_ATTEMPT_TICKS = 80;
|
||||||
|
private static final int MIN_LINK_RANGE = 50;
|
||||||
|
private static final int MAX_LINK_RANGE = 500;
|
||||||
|
private static final int PARTNER_ORIGIN_ATTEMPTS = 8;
|
||||||
|
private static final int PARTNER_SEARCH_ATTEMPTS = 32;
|
||||||
|
private static final int LOCAL_SEARCH_ATTEMPTS = 12;
|
||||||
|
private static final int SEARCH_ATTEMPTS_PER_TICK = 1;
|
||||||
|
private static final int MIN_VERTICAL_SEPARATION = 24;
|
||||||
|
private static final int MIN_PARTNER_VERTICAL_OFFSET = 48;
|
||||||
|
private static final int MAX_PARTNER_VERTICAL_OFFSET = 96;
|
||||||
|
private static final int NATURAL_TEAR_LOCAL_RADIUS = 512;
|
||||||
|
private static final int NATURAL_TEAR_LOCAL_CAP = 6;
|
||||||
|
|
||||||
|
private int attemptTicks;
|
||||||
|
private int localAttempts;
|
||||||
|
private int partnerOriginAttempts;
|
||||||
|
private int partnerSearchAttempts;
|
||||||
|
@Nullable
|
||||||
|
private BlockPos firstPocket;
|
||||||
|
@Nullable
|
||||||
|
private BlockPos partnerOrigin;
|
||||||
|
|
||||||
|
public DimensionalTearMarkerBlockEntity(
|
||||||
|
BlockPos pos,
|
||||||
|
BlockState blockState,
|
||||||
|
Supplier<? extends BlockEntityType<DimensionalTearMarkerBlockEntity>> blockEntityTypeSupplier
|
||||||
|
) {
|
||||||
|
super(blockEntityTypeSupplier.get(), pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, DimensionalTearMarkerBlockEntity blockEntity) {
|
||||||
|
blockEntity.attemptTicks++;
|
||||||
|
boolean spawned = blockEntity.trySpawnLinkedPair(level, pos);
|
||||||
|
if (spawned || blockEntity.attemptTicks >= MAX_ATTEMPT_TICKS) {
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean trySpawnLinkedPair(ServerLevel level, BlockPos origin) {
|
||||||
|
RandomSource random = level.getRandom();
|
||||||
|
for (int i = 0; i < SEARCH_ATTEMPTS_PER_TICK; i++) {
|
||||||
|
if (firstPocket == null) {
|
||||||
|
if (localAttempts >= LOCAL_SEARCH_ATTEMPTS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
localAttempts++;
|
||||||
|
BlockPos candidate = randomCandidate(origin, random);
|
||||||
|
if (isUsablePocket(level, candidate)) {
|
||||||
|
firstPocket = candidate;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partnerOrigin == null && partnerOriginAttempts == 0
|
||||||
|
&& countNearbyTears(level, origin) >= NATURAL_TEAR_LOCAL_CAP) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partnerOrigin == null || partnerSearchAttempts >= PARTNER_SEARCH_ATTEMPTS) {
|
||||||
|
if (partnerOriginAttempts >= PARTNER_ORIGIN_ATTEMPTS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
partnerOrigin = linkedOrigin(firstPocket, random);
|
||||||
|
partnerOriginAttempts++;
|
||||||
|
partnerSearchAttempts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
partnerSearchAttempts++;
|
||||||
|
BlockPos candidate = randomCandidate(partnerOrigin, random);
|
||||||
|
if (Math.abs(candidate.getY() - firstPocket.getY()) < MIN_VERTICAL_SEPARATION
|
||||||
|
|| !isUsablePocket(level, candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return spawnPair(level, random, firstPocket, candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean spawnPair(ServerLevel level, RandomSource random, BlockPos first, BlockPos second) {
|
||||||
|
|
||||||
|
int lifetime = Math.max(1200, AntarchySettings.dimensionalTearLifetimeTicks());
|
||||||
|
float yawA = random.nextFloat() * 360.0F;
|
||||||
|
float yawB = Mth.wrapDegrees(yawA + 140.0F + random.nextFloat() * 80.0F);
|
||||||
|
DimensionalTearEntity tearA = DimensionalTearEntity.create(level, center(first), yawA, lifetime);
|
||||||
|
DimensionalTearEntity tearB = DimensionalTearEntity.create(level, center(second), yawB, lifetime);
|
||||||
|
tearA.linkTo(tearB);
|
||||||
|
tearB.linkTo(tearA);
|
||||||
|
level.addFreshEntity(tearA);
|
||||||
|
level.addFreshEntity(tearB);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockPos randomCandidate(BlockPos origin, RandomSource random) {
|
||||||
|
return origin.offset(
|
||||||
|
Mth.nextInt(random, -16, 16),
|
||||||
|
Mth.nextInt(random, -24, 24),
|
||||||
|
Mth.nextInt(random, -16, 16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockPos linkedOrigin(BlockPos first, RandomSource random) {
|
||||||
|
double angle = random.nextDouble() * Math.PI * 2.0D;
|
||||||
|
int distance = Mth.nextInt(random, MIN_LINK_RANGE, MAX_LINK_RANGE);
|
||||||
|
int dx = Mth.floor(Math.cos(angle) * distance);
|
||||||
|
int dz = Mth.floor(Math.sin(angle) * distance);
|
||||||
|
int verticalOffset = Mth.nextInt(random, MIN_PARTNER_VERTICAL_OFFSET, MAX_PARTNER_VERTICAL_OFFSET);
|
||||||
|
int dy = random.nextBoolean() ? verticalOffset : -verticalOffset;
|
||||||
|
return first.offset(dx, dy, dz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int countNearbyTears(ServerLevel level, BlockPos origin) {
|
||||||
|
AABB area = new AABB(origin).inflate(NATURAL_TEAR_LOCAL_RADIUS);
|
||||||
|
return level.getEntitiesOfClass(DimensionalTearEntity.class, area).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isUsablePocket(ServerLevel level, BlockPos pos) {
|
||||||
|
int minY = level.getMinBuildHeight() + 8;
|
||||||
|
int maxY = level.getMaxBuildHeight() - 8;
|
||||||
|
if (pos.getY() < minY || pos.getY() > maxY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int dx = -2; dx <= 2; dx++) {
|
||||||
|
for (int dy = -2; dy <= 3; dy++) {
|
||||||
|
for (int dz = -4; dz <= 4; dz++) {
|
||||||
|
BlockPos checkPos = pos.offset(dx, dy, dz);
|
||||||
|
if (!level.isLoaded(checkPos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!level.getBlockState(checkPos).getCollisionShape(level, checkPos).isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vec3 center(BlockPos pos) {
|
||||||
|
return new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAdditional(CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
tag.putInt("attempt_ticks", this.attemptTicks);
|
||||||
|
tag.putInt("local_attempts", this.localAttempts);
|
||||||
|
tag.putInt("partner_origin_attempts", this.partnerOriginAttempts);
|
||||||
|
tag.putInt("partner_search_attempts", this.partnerSearchAttempts);
|
||||||
|
if (this.firstPocket != null) {
|
||||||
|
tag.putLong("first_pocket", this.firstPocket.asLong());
|
||||||
|
}
|
||||||
|
if (this.partnerOrigin != null) {
|
||||||
|
tag.putLong("partner_origin", this.partnerOrigin.asLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
this.attemptTicks = tag.getInt("attempt_ticks");
|
||||||
|
this.localAttempts = tag.getInt("local_attempts");
|
||||||
|
this.partnerOriginAttempts = tag.getInt("partner_origin_attempts");
|
||||||
|
this.partnerSearchAttempts = tag.getInt("partner_search_attempts");
|
||||||
|
this.firstPocket = tag.contains("first_pocket") ? BlockPos.of(tag.getLong("first_pocket")) : null;
|
||||||
|
this.partnerOrigin = tag.contains("partner_origin") ? BlockPos.of(tag.getLong("partner_origin")) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class PortalGunPortalBaseBlockEntity extends BlockEntity {
|
||||||
|
private UUID ownerId;
|
||||||
|
private UUID portalId;
|
||||||
|
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
|
private BlockPos masterPos = BlockPos.ZERO;
|
||||||
|
private UUID linkedPortalId;
|
||||||
|
private int pairTime;
|
||||||
|
|
||||||
|
public PortalGunPortalBaseBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<PortalGunPortalBaseBlockEntity>> typeSupplier) {
|
||||||
|
super(typeSupplier.get(), pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configure(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side, BlockPos masterPos) {
|
||||||
|
this.ownerId = ownerId;
|
||||||
|
this.portalId = portalId;
|
||||||
|
this.side = side;
|
||||||
|
this.masterPos = masterPos.immutable();
|
||||||
|
this.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getOwnerId() {
|
||||||
|
return this.ownerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getPortalId() {
|
||||||
|
return this.portalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortalGunPortalEntity.PortalSide getSide() {
|
||||||
|
return this.side;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getMasterPos() {
|
||||||
|
return this.masterPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getLinkedPortalId() {
|
||||||
|
return this.linkedPortalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPairTime() {
|
||||||
|
return this.pairTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePair(UUID linkedPortalId, int pairTime) {
|
||||||
|
this.linkedPortalId = linkedPortalId;
|
||||||
|
this.pairTime = pairTime;
|
||||||
|
this.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side) {
|
||||||
|
return ownerId != null && portalId != null && ownerId.equals(this.ownerId) && portalId.equals(this.portalId) && this.side == side;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBroken() {
|
||||||
|
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||||
|
portal.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalBaseBlockEntity blockEntity) {
|
||||||
|
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master)
|
||||||
|
|| !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)
|
||||||
|
|| !pos.equals(master.getBasePos())) {
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal)
|
||||||
|
|| portal.isRemoved()
|
||||||
|
|| !pos.equals(portal.getBasePos())
|
||||||
|
|| blockEntity.ownerId == null
|
||||||
|
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|
||||||
|
|| blockEntity.side != portal.getPortalSide()) {
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UUID linkedPortalId = portal.getLinkedPortalId();
|
||||||
|
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|
||||||
|
|| (linkedPortalId != null && !linkedPortalId.equals(blockEntity.linkedPortalId))
|
||||||
|
|| blockEntity.pairTime != portal.getPairTime()) {
|
||||||
|
blockEntity.linkedPortalId = linkedPortalId;
|
||||||
|
blockEntity.pairTime = portal.getPairTime();
|
||||||
|
blockEntity.setChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAdditional(CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
if (this.ownerId != null) {
|
||||||
|
tag.putUUID("OwnerId", this.ownerId);
|
||||||
|
}
|
||||||
|
if (this.portalId != null) {
|
||||||
|
tag.putUUID("PortalId", this.portalId);
|
||||||
|
}
|
||||||
|
if (this.linkedPortalId != null) {
|
||||||
|
tag.putUUID("LinkedPortalId", this.linkedPortalId);
|
||||||
|
}
|
||||||
|
tag.putInt("Side", this.side.ordinal());
|
||||||
|
tag.putInt("MasterX", this.masterPos.getX());
|
||||||
|
tag.putInt("MasterY", this.masterPos.getY());
|
||||||
|
tag.putInt("MasterZ", this.masterPos.getZ());
|
||||||
|
tag.putInt("PairTime", this.pairTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
|
||||||
|
this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
|
||||||
|
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
|
||||||
|
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
|
this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
|
||||||
|
this.pairTime = tag.getInt("PairTime");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getUpdateTag() {
|
||||||
|
return this.saveWithoutMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+300
@@ -0,0 +1,300 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public class PortalGunPortalMasterBlockEntity extends BlockEntity {
|
||||||
|
private UUID ownerId;
|
||||||
|
private UUID portalId;
|
||||||
|
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
|
private Direction facing = Direction.NORTH;
|
||||||
|
private Direction upAxis = Direction.UP;
|
||||||
|
private BlockPos basePos = BlockPos.ZERO;
|
||||||
|
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
|
||||||
|
private Set<BlockPos> compensatedSpots = Set.of();
|
||||||
|
private UUID linkedPortalId;
|
||||||
|
private int pairTime;
|
||||||
|
|
||||||
|
public PortalGunPortalMasterBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<PortalGunPortalMasterBlockEntity>> typeSupplier) {
|
||||||
|
super(typeSupplier.get(), pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configure(
|
||||||
|
UUID ownerId,
|
||||||
|
UUID portalId,
|
||||||
|
PortalGunPortalEntity.PortalSide side,
|
||||||
|
Direction facing,
|
||||||
|
Direction upAxis,
|
||||||
|
BlockPos basePos,
|
||||||
|
BlockPos[] portalSpots,
|
||||||
|
Set<BlockPos> compensatedSpots,
|
||||||
|
int pairTime
|
||||||
|
) {
|
||||||
|
this.ownerId = ownerId;
|
||||||
|
this.portalId = portalId;
|
||||||
|
this.side = side;
|
||||||
|
this.facing = facing;
|
||||||
|
this.upAxis = upAxis;
|
||||||
|
this.basePos = basePos.immutable();
|
||||||
|
this.portalSpots = new BlockPos[] {portalSpots[0].immutable(), portalSpots[1].immutable()};
|
||||||
|
this.compensatedSpots = new HashSet<>(compensatedSpots);
|
||||||
|
this.pairTime = pairTime;
|
||||||
|
this.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getPortalId() {
|
||||||
|
return this.portalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getOwnerId() {
|
||||||
|
return this.ownerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortalGunPortalEntity.PortalSide getSide() {
|
||||||
|
return this.side;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getFacing() {
|
||||||
|
return this.facing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getUpAxis() {
|
||||||
|
return this.upAxis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getBasePos() {
|
||||||
|
return this.basePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos[] getPortalSpots() {
|
||||||
|
return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<BlockPos> getCompensatedSpots() {
|
||||||
|
return Set.copyOf(this.compensatedSpots);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPairTime() {
|
||||||
|
return this.pairTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getLinkedPortalId() {
|
||||||
|
return this.linkedPortalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePair(UUID linkedPortalId, int pairTime) {
|
||||||
|
this.linkedPortalId = linkedPortalId;
|
||||||
|
this.pairTime = pairTime;
|
||||||
|
this.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side) {
|
||||||
|
return ownerId != null && portalId != null && ownerId.equals(this.ownerId) && portalId.equals(this.portalId) && this.side == side;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBroken() {
|
||||||
|
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||||
|
portal.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalMasterBlockEntity blockEntity) {
|
||||||
|
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!blockEntity.hasValidFootprint(level, pos)) {
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PortalGunPortalEntity portal = blockEntity.ensurePortalEntity(level);
|
||||||
|
if (portal == null
|
||||||
|
|| portal.isRemoved()
|
||||||
|
|| !pos.equals(portal.getMasterPos())
|
||||||
|
|| blockEntity.ownerId == null
|
||||||
|
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|
||||||
|
|| blockEntity.side != portal.getPortalSide()) {
|
||||||
|
level.removeBlock(pos, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
blockEntity.ensureLinkedPortal(level, portal);
|
||||||
|
UUID linkedPortalId = portal.getLinkedPortalId();
|
||||||
|
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|
||||||
|
|| (linkedPortalId != null && !linkedPortalId.equals(blockEntity.linkedPortalId))
|
||||||
|
|| blockEntity.pairTime != portal.getPairTime()) {
|
||||||
|
blockEntity.linkedPortalId = linkedPortalId;
|
||||||
|
blockEntity.pairTime = portal.getPairTime();
|
||||||
|
blockEntity.setChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasValidFootprint(ServerLevel level, BlockPos pos) {
|
||||||
|
if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(level.getBlockEntity(this.basePos) instanceof PortalGunPortalBaseBlockEntity base)
|
||||||
|
|| !base.matches(this.ownerId, this.portalId, this.side)
|
||||||
|
|| !pos.equals(base.getMasterPos())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<BlockPos> supportPositions = new ArrayList<>(2);
|
||||||
|
for (BlockPos portalSpot : this.portalSpots) {
|
||||||
|
if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
supportPositions.add(portalSpot.relative(this.facing.getOpposite()));
|
||||||
|
}
|
||||||
|
for (BlockPos supportPos : supportPositions) {
|
||||||
|
BlockState supportState = level.getBlockState(supportPos);
|
||||||
|
if (!supportState.isFaceSturdy(level, supportPos, this.facing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PortalGunPortalEntity ensurePortalEntity(ServerLevel level) {
|
||||||
|
if (this.portalId == null || this.ownerId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
|
||||||
|
return portal;
|
||||||
|
}
|
||||||
|
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(new ResourceLocation(Antarchy.MODID, "portal_gun_portal"));
|
||||||
|
if (!(rawType instanceof EntityType<?>)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.compensatedSpots);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
|
||||||
|
restored.setUUID(this.portalId);
|
||||||
|
restored.configure(this.ownerId, this.side, placement);
|
||||||
|
restored.restorePair(this.linkedPortalId, this.pairTime);
|
||||||
|
Vec3 center = placement.center();
|
||||||
|
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
|
||||||
|
level.addFreshEntity(restored);
|
||||||
|
PortalGunSavedData.setPortal(level.getServer(), this.ownerId, this.side, restored.getUUID());
|
||||||
|
return restored;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureLinkedPortal(ServerLevel level, PortalGunPortalEntity portal) {
|
||||||
|
if (this.ownerId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
|
||||||
|
level.getServer(),
|
||||||
|
this.ownerId,
|
||||||
|
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
|
||||||
|
);
|
||||||
|
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!counterpart.getUUID().equals(portal.getLinkedPortalId()) || !portal.getUUID().equals(counterpart.getLinkedPortalId())) {
|
||||||
|
portal.linkTo(counterpart);
|
||||||
|
counterpart.linkTo(portal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAdditional(CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
if (this.ownerId != null) {
|
||||||
|
tag.putUUID("OwnerId", this.ownerId);
|
||||||
|
}
|
||||||
|
if (this.portalId != null) {
|
||||||
|
tag.putUUID("PortalId", this.portalId);
|
||||||
|
}
|
||||||
|
if (this.linkedPortalId != null) {
|
||||||
|
tag.putUUID("LinkedPortalId", this.linkedPortalId);
|
||||||
|
}
|
||||||
|
tag.putInt("Side", this.side.ordinal());
|
||||||
|
tag.putInt("Facing", this.facing.get3DDataValue());
|
||||||
|
tag.putInt("UpAxis", this.upAxis.get3DDataValue());
|
||||||
|
tag.putInt("BaseX", this.basePos.getX());
|
||||||
|
tag.putInt("BaseY", this.basePos.getY());
|
||||||
|
tag.putInt("BaseZ", this.basePos.getZ());
|
||||||
|
tag.putInt("PairTime", this.pairTime);
|
||||||
|
ListTag spots = new ListTag();
|
||||||
|
for (BlockPos portalSpot : this.portalSpots) {
|
||||||
|
CompoundTag spot = new CompoundTag();
|
||||||
|
spot.putInt("X", portalSpot.getX());
|
||||||
|
spot.putInt("Y", portalSpot.getY());
|
||||||
|
spot.putInt("Z", portalSpot.getZ());
|
||||||
|
spots.add(spot);
|
||||||
|
}
|
||||||
|
tag.put("PortalSpots", spots);
|
||||||
|
ListTag compensated = new ListTag();
|
||||||
|
for (BlockPos compensatedSpot : this.compensatedSpots) {
|
||||||
|
CompoundTag spot = new CompoundTag();
|
||||||
|
spot.putInt("X", compensatedSpot.getX());
|
||||||
|
spot.putInt("Y", compensatedSpot.getY());
|
||||||
|
spot.putInt("Z", compensatedSpot.getZ());
|
||||||
|
compensated.add(spot);
|
||||||
|
}
|
||||||
|
tag.put("CompensatedSpots", compensated);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
|
||||||
|
this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
|
||||||
|
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
|
||||||
|
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
|
||||||
|
this.facing = Direction.from3DDataValue(tag.getInt("Facing"));
|
||||||
|
this.upAxis = Direction.from3DDataValue(tag.getInt("UpAxis"));
|
||||||
|
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
|
||||||
|
this.pairTime = tag.getInt("PairTime");
|
||||||
|
ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < Math.min(2, spots.size()); i++) {
|
||||||
|
CompoundTag spot = spots.getCompound(i);
|
||||||
|
this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"));
|
||||||
|
}
|
||||||
|
ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
|
||||||
|
Set<BlockPos> compensatedSpots = new HashSet<>();
|
||||||
|
for (int i = 0; i < compensated.size(); i++) {
|
||||||
|
CompoundTag spot = compensated.getCompound(i);
|
||||||
|
compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")));
|
||||||
|
}
|
||||||
|
this.compensatedSpots = compensatedSpots;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getUpdateTag() {
|
||||||
|
return this.saveWithoutMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class RoyalEggBlockEntity extends BlockEntity {
|
||||||
|
@Nullable
|
||||||
|
private UUID placerUuid;
|
||||||
|
|
||||||
|
public RoyalEggBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
super(AntarchyObjects.ROYAL_EGG_BLOCK_ENTITY.get(), pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UUID getPlacerUuid() {
|
||||||
|
return this.placerUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlacerUuid(@Nullable UUID placerUuid) {
|
||||||
|
this.placerUuid = placerUuid;
|
||||||
|
this.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
this.placerUuid = tag.hasUUID("Placer") ? tag.getUUID("Placer") : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAdditional(CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
if (this.placerUuid != null) {
|
||||||
|
tag.putUUID("Placer", this.placerUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
package com.craisinlord.antarchy.content.block.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.block.BluestoneSignalHelper;
|
||||||
|
import com.craisinlord.antarchy.content.block.VortexLensBlock;
|
||||||
|
import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class VortexLensBlockEntity extends BlockEntity {
|
||||||
|
private static final String ACTIVE_VORTEX_KEY = "ActiveVortex";
|
||||||
|
private static final int LENS_VORTEX_REFRESH_DURATION = 40;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private UUID activeVortexUuid;
|
||||||
|
private boolean refreshNow = true;
|
||||||
|
|
||||||
|
public VortexLensBlockEntity(BlockPos pos, BlockState state,
|
||||||
|
Supplier<? extends BlockEntityType<VortexLensBlockEntity>> typeSupplier) {
|
||||||
|
super(typeSupplier.get(), pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, VortexLensBlockEntity blockEntity) {
|
||||||
|
blockEntity.tickLens(level, pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshImmediately() {
|
||||||
|
this.refreshNow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeActiveVortex() {
|
||||||
|
if (this.level instanceof ServerLevel serverLevel) {
|
||||||
|
WindVortexEntity vortex = this.getActiveVortex(serverLevel);
|
||||||
|
if (vortex != null) {
|
||||||
|
vortex.removeFromLens();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.activeVortexUuid = null;
|
||||||
|
this.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickLens(ServerLevel level, BlockPos pos, BlockState state) {
|
||||||
|
if (!this.refreshNow && (level.getGameTime() & 1L) == 1L) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.refreshNow = false;
|
||||||
|
|
||||||
|
int bluestoneSignal = BluestoneSignalHelper.getBestNeighborSignal(level, pos);
|
||||||
|
int redstoneSignal = level.getBestNeighborSignal(pos);
|
||||||
|
boolean pushMode = bluestoneSignal > 0;
|
||||||
|
int signal = pushMode ? bluestoneSignal : redstoneSignal;
|
||||||
|
|
||||||
|
if (signal <= 0) {
|
||||||
|
this.removeActiveVortex();
|
||||||
|
this.updateBlockState(level, pos, state, false, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindVortexEntity vortex = this.getOrCreateVortex(level, pos, state);
|
||||||
|
if (vortex == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction facing = state.getValue(VortexLensBlock.FACING);
|
||||||
|
double signalScale = Mth.clamp(signal / 15.0D, 0.0D, 1.0D);
|
||||||
|
double radius = Mth.lerp(signalScale, AntarchySettings.vortexLensMinRadius(), AntarchySettings.vortexLensMaxRadius());
|
||||||
|
double height = Mth.lerp(signalScale, AntarchySettings.vortexLensMinHeight(), AntarchySettings.vortexLensMaxHeight());
|
||||||
|
Vec3 axis = Vec3.atLowerCornerOf(facing.getNormal());
|
||||||
|
Vec3 origin = Vec3.atCenterOf(pos).add(axis.scale(0.65D));
|
||||||
|
WindVortexEntity.VortexMode mode = pushMode ? WindVortexEntity.VortexMode.LENS_PUSH : WindVortexEntity.VortexMode.LENS_PULL;
|
||||||
|
double strength = pushMode ? AntarchySettings.vortexLensPushStrength() : AntarchySettings.vortexLensPullStrength();
|
||||||
|
|
||||||
|
vortex.setPos(origin.x, origin.y, origin.z);
|
||||||
|
vortex.setDeltaMovement(Vec3.ZERO);
|
||||||
|
vortex.setAxis(facing);
|
||||||
|
vortex.setMode(mode);
|
||||||
|
vortex.setVortexSize((float) height, (float) radius);
|
||||||
|
vortex.setVortexDurationTicks(LENS_VORTEX_REFRESH_DURATION);
|
||||||
|
vortex.resetVortexAge();
|
||||||
|
vortex.setVortexStrengths(strength, AntarchySettings.vortexLensLaunchStrength());
|
||||||
|
vortex.setDamaging(false);
|
||||||
|
this.updateBlockState(level, pos, state, true, pushMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private WindVortexEntity getOrCreateVortex(ServerLevel level, BlockPos pos, BlockState state) {
|
||||||
|
WindVortexEntity vortex = this.getActiveVortex(level);
|
||||||
|
if (vortex != null) {
|
||||||
|
return vortex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction facing = state.getValue(VortexLensBlock.FACING);
|
||||||
|
Vec3 axis = Vec3.atLowerCornerOf(facing.getNormal());
|
||||||
|
Vec3 origin = Vec3.atCenterOf(pos).add(axis.scale(0.65D));
|
||||||
|
vortex = WindVortexEntity.create(level, AntarchyObjects.WIND_VORTEX.get(), origin, Vec3.ZERO, null, false);
|
||||||
|
vortex.setAxis(facing);
|
||||||
|
vortex.setMode(WindVortexEntity.VortexMode.LENS_PULL);
|
||||||
|
vortex.setVortexDurationTicks(LENS_VORTEX_REFRESH_DURATION);
|
||||||
|
vortex.resetVortexAge();
|
||||||
|
level.addFreshEntity(vortex);
|
||||||
|
this.activeVortexUuid = vortex.getUUID();
|
||||||
|
this.setChanged();
|
||||||
|
return vortex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private WindVortexEntity getActiveVortex(ServerLevel level) {
|
||||||
|
if (this.activeVortexUuid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Entity entity = level.getEntity(this.activeVortexUuid);
|
||||||
|
if (entity instanceof WindVortexEntity vortex && vortex.isAlive()) {
|
||||||
|
return vortex;
|
||||||
|
}
|
||||||
|
this.activeVortexUuid = null;
|
||||||
|
this.setChanged();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBlockState(ServerLevel level, BlockPos pos, BlockState state, boolean powered, boolean pushing) {
|
||||||
|
if (!(state.getBlock() instanceof VortexLensBlock)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.getValue(VortexLensBlock.POWERED) != powered || state.getValue(VortexLensBlock.PUSHING) != pushing) {
|
||||||
|
level.setBlock(pos,
|
||||||
|
state.setValue(VortexLensBlock.POWERED, powered).setValue(VortexLensBlock.PUSHING, pushing),
|
||||||
|
Block.UPDATE_CLIENTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAdditional(CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
if (this.activeVortexUuid != null) {
|
||||||
|
tag.putUUID(ACTIVE_VORTEX_KEY, this.activeVortexUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
this.activeVortexUuid = tag.hasUUID(ACTIVE_VORTEX_KEY) ? tag.getUUID(ACTIVE_VORTEX_KEY) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshot;
|
||||||
|
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshotSampler;
|
||||||
|
import com.craisinlord.antarchy.content.time.TimeDilationMath;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
|
public final class ClientTimeDilationTicker {
|
||||||
|
private static volatile List<TimeDilationFieldSnapshot> activeFields = List.of();
|
||||||
|
private static ClientLevel activeLevel;
|
||||||
|
private static final Map<ItemEntity, ItemSpinState> ITEM_SPIN_STATES = new WeakHashMap<>();
|
||||||
|
private static final Map<LivingEntity, AttackAnimationState> ATTACK_ANIMATION_STATES = new WeakHashMap<>();
|
||||||
|
private static final Map<Entity, AnimationClockState> ANIMATION_CLOCKS = new WeakHashMap<>();
|
||||||
|
|
||||||
|
private ClientTimeDilationTicker() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick(ClientLevel level) {
|
||||||
|
if (activeLevel != level) {
|
||||||
|
activeLevel = level;
|
||||||
|
activeFields = List.of();
|
||||||
|
ITEM_SPIN_STATES.clear();
|
||||||
|
ATTACK_ANIMATION_STATES.clear();
|
||||||
|
ANIMATION_CLOCKS.clear();
|
||||||
|
com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dilateAnimationTime(Entity entity, float vanillaTime) {
|
||||||
|
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
|
||||||
|
if (rate >= TimeDilationMath.NORMAL_RATE) {
|
||||||
|
ANIMATION_CLOCKS.remove(entity);
|
||||||
|
return vanillaTime;
|
||||||
|
}
|
||||||
|
AnimationClockState state = ANIMATION_CLOCKS.computeIfAbsent(entity,
|
||||||
|
ignored -> new AnimationClockState(vanillaTime));
|
||||||
|
float delta = vanillaTime - state.lastVanillaTime;
|
||||||
|
if (delta >= 0.0F) {
|
||||||
|
state.temporalTime += delta * (float) rate;
|
||||||
|
}
|
||||||
|
state.lastVanillaTime = vanillaTime;
|
||||||
|
return state.temporalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dilateAttackAnimation(LivingEntity entity, float partialTick, float vanillaAnimation) {
|
||||||
|
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
|
||||||
|
if (rate >= TimeDilationMath.NORMAL_RATE) {
|
||||||
|
ATTACK_ANIMATION_STATES.remove(entity);
|
||||||
|
return vanillaAnimation;
|
||||||
|
}
|
||||||
|
AttackAnimationState state = ATTACK_ANIMATION_STATES.computeIfAbsent(
|
||||||
|
entity, ignored -> new AttackAnimationState(entity.tickCount, vanillaAnimation)
|
||||||
|
);
|
||||||
|
if (state.lastTick != entity.tickCount) {
|
||||||
|
state.previous = state.current;
|
||||||
|
state.current += (vanillaAnimation - state.current) * (float) rate;
|
||||||
|
state.lastTick = entity.tickCount;
|
||||||
|
}
|
||||||
|
return state.previous + (state.current - state.previous) * partialTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dilateRotation(LivingEntity entity, float previous, float current, float partialTick) {
|
||||||
|
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
|
||||||
|
if (rate >= TimeDilationMath.NORMAL_RATE) {
|
||||||
|
return Mth.rotLerp(partialTick, previous, current);
|
||||||
|
}
|
||||||
|
return previous + Mth.wrapDegrees(current - previous) * partialTick * (float) rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dilatePitch(LivingEntity entity, float previous, float current, float partialTick) {
|
||||||
|
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
|
||||||
|
if (rate >= TimeDilationMath.NORMAL_RATE) {
|
||||||
|
return Mth.lerp(partialTick, previous, current);
|
||||||
|
}
|
||||||
|
return previous + (current - previous) * partialTick * (float) rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dilateWalkAmount(LivingEntity entity, float amount) {
|
||||||
|
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
|
||||||
|
return rate >= TimeDilationMath.NORMAL_RATE ? amount : amount * (float) rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dilateItemSpin(ItemEntity item, float vanillaSpin) {
|
||||||
|
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item);
|
||||||
|
if (rate >= TimeDilationMath.NORMAL_RATE) {
|
||||||
|
ITEM_SPIN_STATES.remove(item);
|
||||||
|
return vanillaSpin;
|
||||||
|
}
|
||||||
|
ItemSpinState state = ITEM_SPIN_STATES.computeIfAbsent(item, ignored -> new ItemSpinState(vanillaSpin));
|
||||||
|
float rawDelta = vanillaSpin - state.lastVanillaSpin;
|
||||||
|
state.lastVanillaSpin = vanillaSpin;
|
||||||
|
state.temporalSpin += rawDelta * (float) rate;
|
||||||
|
return state.temporalSpin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ItemSpinState {
|
||||||
|
private float lastVanillaSpin;
|
||||||
|
private float temporalSpin;
|
||||||
|
|
||||||
|
private ItemSpinState(float initialSpin) {
|
||||||
|
this.lastVanillaSpin = initialSpin;
|
||||||
|
this.temporalSpin = initialSpin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AttackAnimationState {
|
||||||
|
private int lastTick;
|
||||||
|
private float previous;
|
||||||
|
private float current;
|
||||||
|
|
||||||
|
private AttackAnimationState(int tick, float initial) {
|
||||||
|
this.lastTick = tick;
|
||||||
|
this.previous = initial;
|
||||||
|
this.current = initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AnimationClockState {
|
||||||
|
private float lastVanillaTime;
|
||||||
|
private float temporalTime;
|
||||||
|
|
||||||
|
private AnimationClockState(float initialTime) {
|
||||||
|
this.lastVanillaTime = initialTime;
|
||||||
|
this.temporalTime = initialTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double rateAt(double x, double y, double z) {
|
||||||
|
List<TimeDilationFieldSnapshot> fields = activeFields;
|
||||||
|
if (fields.isEmpty()) {
|
||||||
|
return TimeDilationMath.NORMAL_RATE;
|
||||||
|
}
|
||||||
|
return TimeDilationFieldSnapshotSampler.sample(fields, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyFields(List<TimeDilationFieldSnapshot> fields) {
|
||||||
|
activeFields = List.copyOf(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
|
||||||
|
public final class ContractionAfterimages {
|
||||||
|
public static final int MAX_SAMPLES = 10;
|
||||||
|
public static final int SAMPLE_LIFETIME_TICKS = 13;
|
||||||
|
private static final Map<LivingEntity, Deque<Sample>> HISTORY = new WeakHashMap<>();
|
||||||
|
|
||||||
|
private ContractionAfterimages() {}
|
||||||
|
|
||||||
|
public static void tick(ClientLevel level) {
|
||||||
|
MobEffect contracted = RoyalEffectHooks.contractedHolder();
|
||||||
|
int now = (int) (level.getGameTime() & 0x7fffffffL);
|
||||||
|
Iterator<Map.Entry<LivingEntity, Deque<Sample>>> iterator = HISTORY.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
LivingEntity tracked = iterator.next().getKey();
|
||||||
|
if (tracked == null || tracked.isRemoved() || !tracked.isAlive() || contracted == null
|
||||||
|
|| !tracked.hasEffect(contracted)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contracted == null) return;
|
||||||
|
for (Entity entity : level.entitiesForRendering()) {
|
||||||
|
if (!(entity instanceof LivingEntity living) || living.isInvisible() || !living.hasEffect(contracted)) continue;
|
||||||
|
Deque<Sample> samples = HISTORY.computeIfAbsent(living, ignored -> new ArrayDeque<>());
|
||||||
|
samples.addFirst(new Sample(living, now));
|
||||||
|
while (samples.size() > MAX_SAMPLES) samples.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Deque<Sample> samples(LivingEntity entity) { return HISTORY.get(entity); }
|
||||||
|
|
||||||
|
public static float fade(Sample sample, int now) {
|
||||||
|
return Math.max(0.0F, 1.0F - (now - sample.recordedTick) / (float) SAMPLE_LIFETIME_TICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() { HISTORY.clear(); }
|
||||||
|
|
||||||
|
public static final class Sample {
|
||||||
|
public final double x, y, z;
|
||||||
|
public final float bodyYaw, headYaw, xRot, limbPos, limbSpeed, ageInTicks;
|
||||||
|
public final int recordedTick;
|
||||||
|
|
||||||
|
Sample(LivingEntity entity, int recordedTick) {
|
||||||
|
this.x = entity.getX(); this.y = entity.getY(); this.z = entity.getZ();
|
||||||
|
this.bodyYaw = entity.yBodyRot; this.headYaw = entity.getYHeadRot(); this.xRot = entity.getXRot();
|
||||||
|
this.limbPos = entity.walkAnimation.position();
|
||||||
|
this.limbSpeed = Math.min(entity.walkAnimation.speed(), 1.0F);
|
||||||
|
this.ageInTicks = entity.tickCount; this.recordedTick = recordedTick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+294
@@ -0,0 +1,294 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
|
import com.mojang.blaze3d.vertex.BufferUploader;
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import net.minecraft.client.Camera;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class HoverboardTrailSystem {
|
||||||
|
private static final int MAX_TRAIL_POINTS = 18;
|
||||||
|
private static final int TRAIL_LIFETIME_TICKS = 12;
|
||||||
|
private static final float CORE_HALF_WIDTH = 0.0225f;
|
||||||
|
private static final float GLOW_HALF_WIDTH = 0.055f;
|
||||||
|
private static final double MIN_DISTANCE_BETWEEN_POINTS = 0.04D;
|
||||||
|
private static final double LATERAL_OFFSET = 0.5275D;
|
||||||
|
private static final double REAR_OFFSET = 0.77D;
|
||||||
|
private static final double HEIGHT_OFFSET = 0.14D;
|
||||||
|
private static final float[] DEFAULT_COLOR = rgb(0xC9A0FF);
|
||||||
|
|
||||||
|
private static final Map<Integer, BoardTrails> TRAILS = new HashMap<>();
|
||||||
|
|
||||||
|
private HoverboardTrailSystem() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TrailPoint {
|
||||||
|
final Vec3 pos;
|
||||||
|
int age;
|
||||||
|
|
||||||
|
TrailPoint(Vec3 pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
float progress() {
|
||||||
|
return (float) this.age / (float) TRAIL_LIFETIME_TICKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean expired() {
|
||||||
|
return this.age >= TRAIL_LIFETIME_TICKS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class BoardTrails {
|
||||||
|
final ArrayDeque<TrailPoint> left = new ArrayDeque<>();
|
||||||
|
final ArrayDeque<TrailPoint> right = new ArrayDeque<>();
|
||||||
|
float[] color = DEFAULT_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick() {
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (mc.level == null) {
|
||||||
|
TRAILS.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mc.isPaused()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Integer> activeIds = new HashSet<>();
|
||||||
|
for (Entity entity : mc.level.entitiesForRendering()) {
|
||||||
|
if (!(entity instanceof HoverboardEntity hoverboard)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = hoverboard.getId();
|
||||||
|
activeIds.add(id);
|
||||||
|
BoardTrails boardTrails = TRAILS.computeIfAbsent(id, ignored -> new BoardTrails());
|
||||||
|
boardTrails.color = colorFor(hoverboard.getColor());
|
||||||
|
|
||||||
|
if (hoverboard.isVehicle() && hoverboard.isTrailActive()) {
|
||||||
|
TrailAnchors anchors = trailAnchors(hoverboard);
|
||||||
|
addPoint(boardTrails.left, anchors.left());
|
||||||
|
addPoint(boardTrails.right, anchors.right());
|
||||||
|
}
|
||||||
|
|
||||||
|
age(boardTrails.left);
|
||||||
|
age(boardTrails.right);
|
||||||
|
if (boardTrails.left.isEmpty() && boardTrails.right.isEmpty() && !hoverboard.isTrailActive()) {
|
||||||
|
TRAILS.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TRAILS.keySet().removeIf(id -> !activeIds.contains(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void render(Camera camera, Matrix4f mat) {
|
||||||
|
if (TRAILS.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (mc.level == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 camPos = camera.getPosition();
|
||||||
|
List<TrailSnapshot> snapshots = new ArrayList<>(TRAILS.size() * 2);
|
||||||
|
for (BoardTrails boardTrails : TRAILS.values()) {
|
||||||
|
if (boardTrails.left.size() >= 2) {
|
||||||
|
snapshots.add(new TrailSnapshot(new ArrayList<>(boardTrails.left), boardTrails.color));
|
||||||
|
}
|
||||||
|
if (boardTrails.right.size() >= 2) {
|
||||||
|
snapshots.add(new TrailSnapshot(new ArrayList<>(boardTrails.right), boardTrails.color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshots.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.depthMask(false);
|
||||||
|
RenderSystem.disableCull();
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionColorShader);
|
||||||
|
|
||||||
|
Tesselator tesselator = Tesselator.getInstance();
|
||||||
|
|
||||||
|
BufferBuilder glowBuf = tesselator.getBuilder();
|
||||||
|
glowBuf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||||
|
for (TrailSnapshot snapshot : snapshots) {
|
||||||
|
buildPass(glowBuf, mat, snapshot.points(), snapshot.color(), camPos, true);
|
||||||
|
}
|
||||||
|
BufferBuilder.RenderedBuffer glowMesh = glowBuf.end();
|
||||||
|
if (glowMesh != null) {
|
||||||
|
BufferUploader.drawWithShader(glowMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferBuilder coreBuf = tesselator.getBuilder();
|
||||||
|
coreBuf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||||
|
for (TrailSnapshot snapshot : snapshots) {
|
||||||
|
buildPass(coreBuf, mat, snapshot.points(), snapshot.color(), camPos, false);
|
||||||
|
}
|
||||||
|
BufferBuilder.RenderedBuffer coreMesh = coreBuf.end();
|
||||||
|
if (coreMesh != null) {
|
||||||
|
BufferUploader.drawWithShader(coreMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderSystem.enableCull();
|
||||||
|
RenderSystem.depthMask(true);
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addPoint(ArrayDeque<TrailPoint> points, Vec3 pos) {
|
||||||
|
if (points.isEmpty()) {
|
||||||
|
points.addFirst(new TrailPoint(pos));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (points.peekFirst().pos.distanceToSqr(pos) < MIN_DISTANCE_BETWEEN_POINTS * MIN_DISTANCE_BETWEEN_POINTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
points.addFirst(new TrailPoint(pos));
|
||||||
|
while (points.size() > MAX_TRAIL_POINTS) {
|
||||||
|
points.pollLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void age(ArrayDeque<TrailPoint> points) {
|
||||||
|
Iterator<TrailPoint> iterator = points.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
TrailPoint point = iterator.next();
|
||||||
|
point.age++;
|
||||||
|
if (point.expired()) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TrailAnchors trailAnchors(HoverboardEntity hoverboard) {
|
||||||
|
float yawRad = hoverboard.getYRot() * Mth.DEG_TO_RAD;
|
||||||
|
Vec3 forward = new Vec3(-Mth.sin(yawRad), 0.0D, Mth.cos(yawRad));
|
||||||
|
if (forward.lengthSqr() < 1.0E-6D) {
|
||||||
|
forward = new Vec3(0.0D, 0.0D, 1.0D);
|
||||||
|
}
|
||||||
|
forward = forward.normalize();
|
||||||
|
Vec3 right = new Vec3(forward.z, 0.0D, -forward.x);
|
||||||
|
Vec3 center = hoverboard.position().add(0.0D, HEIGHT_OFFSET, 0.0D).subtract(forward.scale(REAR_OFFSET));
|
||||||
|
return new TrailAnchors(center.subtract(right.scale(LATERAL_OFFSET)), center.add(right.scale(LATERAL_OFFSET)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void buildPass(BufferBuilder buf, Matrix4f mat, List<TrailPoint> points, float[] color, Vec3 camPos, boolean glow) {
|
||||||
|
int n = points.size();
|
||||||
|
for (int i = 0; i < n - 1; i++) {
|
||||||
|
TrailPoint newer = points.get(i);
|
||||||
|
TrailPoint older = points.get(i + 1);
|
||||||
|
|
||||||
|
float alphaNearer = (glow ? 0.34f : 0.78f) * indexAlpha(i, n) * (1.0f - newer.progress());
|
||||||
|
float alphaFarther = (glow ? 0.34f : 0.78f) * indexAlpha(i + 1, n) * (1.0f - older.progress());
|
||||||
|
if (alphaNearer < 0.005f && alphaFarther < 0.005f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 right = cameraFacingRight(newer.pos, older.pos, camPos);
|
||||||
|
if (right == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
right = right.scale(glow ? GLOW_HALF_WIDTH : CORE_HALF_WIDTH);
|
||||||
|
Vec3 na = newer.pos.subtract(camPos);
|
||||||
|
Vec3 nb = older.pos.subtract(camPos);
|
||||||
|
|
||||||
|
buf.vertex(mat, (float) (na.x + right.x), (float) (na.y + right.y), (float) (na.z + right.z)).color(color[0], color[1], color[2], alphaNearer).endVertex();
|
||||||
|
buf.vertex(mat, (float) (na.x - right.x), (float) (na.y - right.y), (float) (na.z - right.z)).color(color[0], color[1], color[2], alphaNearer).endVertex();
|
||||||
|
buf.vertex(mat, (float) (nb.x - right.x), (float) (nb.y - right.y), (float) (nb.z - right.z)).color(color[0], color[1], color[2], alphaFarther).endVertex();
|
||||||
|
buf.vertex(mat, (float) (nb.x + right.x), (float) (nb.y + right.y), (float) (nb.z + right.z)).color(color[0], color[1], color[2], alphaFarther).endVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vec3 cameraFacingRight(Vec3 a, Vec3 b, Vec3 camPos) {
|
||||||
|
Vec3 segDir = b.subtract(a);
|
||||||
|
if (segDir.lengthSqr() < 1.0E-12D) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
segDir = segDir.normalize();
|
||||||
|
Vec3 mid = a.add(b).scale(0.5D);
|
||||||
|
Vec3 toCamera = camPos.subtract(mid);
|
||||||
|
if (toCamera.lengthSqr() < 1.0E-12D) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
toCamera = toCamera.normalize();
|
||||||
|
Vec3 right = segDir.cross(toCamera);
|
||||||
|
if (right.lengthSqr() < 1.0E-10D) {
|
||||||
|
right = segDir.cross(new Vec3(0.0D, 1.0D, 0.0D));
|
||||||
|
if (right.lengthSqr() < 1.0E-10D) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return right.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float indexAlpha(int i, int n) {
|
||||||
|
if (n <= 1) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
return 1.0f - (float) i / (float) (n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float[] colorFor(DyeColor color) {
|
||||||
|
if (color == null) {
|
||||||
|
return DEFAULT_COLOR;
|
||||||
|
}
|
||||||
|
return switch (color) {
|
||||||
|
case PURPLE -> rgb(0x00FF3C);
|
||||||
|
case WHITE -> rgb(0xFF1919);
|
||||||
|
case ORANGE -> rgb(0x33CCFF);
|
||||||
|
case LIGHT_BLUE -> rgb(0xFF8A00);
|
||||||
|
case YELLOW -> rgb(0xB026FF);
|
||||||
|
case PINK -> rgb(0x66E0FF);
|
||||||
|
case LIME -> rgb(0xFF00E6);
|
||||||
|
case GRAY -> rgb(0xFF9F4D);
|
||||||
|
case LIGHT_GRAY -> rgb(0x9BFF1A);
|
||||||
|
case BLUE -> rgb(0x2ED9A0);
|
||||||
|
case BROWN -> rgb(0x66C6FF);
|
||||||
|
case GREEN -> rgb(0xFF3B1F);
|
||||||
|
case RED -> rgb(0xFF7A00);
|
||||||
|
case BLACK -> rgb(0xFFFFFF);
|
||||||
|
case MAGENTA -> rgb(0x00E5FF);
|
||||||
|
case CYAN -> rgb(0xFF3DDB);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float[] rgb(int color) {
|
||||||
|
return new float[] {
|
||||||
|
((color >> 16) & 255) / 255.0f,
|
||||||
|
((color >> 8) & 255) / 255.0f,
|
||||||
|
(color & 255) / 255.0f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TrailAnchors(Vec3 left, Vec3 right) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TrailSnapshot(List<TrailPoint> points, float[] color) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public final class PortalGunEntityTransformationStack {
|
||||||
|
private final Entity entity;
|
||||||
|
private final Deque<EntityTransformation> stack = new ArrayDeque<>();
|
||||||
|
|
||||||
|
public PortalGunEntityTransformationStack(Entity entity) {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push() {
|
||||||
|
this.stack.push(EntityTransformation.capture(this.entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pop() {
|
||||||
|
if (!this.stack.isEmpty()) {
|
||||||
|
this.stack.pop().restore(this.entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 moveEntity(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, float partialTick) {
|
||||||
|
Vec3 interpolated = new Vec3(
|
||||||
|
net.minecraft.util.Mth.lerp(partialTick, this.entity.xo, this.entity.getX()),
|
||||||
|
net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()),
|
||||||
|
net.minecraft.util.Mth.lerp(partialTick, this.entity.zo, this.entity.getZ())
|
||||||
|
);
|
||||||
|
Vec3 previous = new Vec3(this.entity.xo, this.entity.yo, this.entity.zo);
|
||||||
|
Vec3 transformed = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolated.subtract(sourcePortal.position())));
|
||||||
|
Vec3 transformedPrevious = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previous.subtract(sourcePortal.position())));
|
||||||
|
Vec3 look = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getLookAngle()).normalize();
|
||||||
|
Vec3 velocity = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getDeltaMovement());
|
||||||
|
float yaw = PortalGunTransformUtil.yawFromLook(look);
|
||||||
|
float pitch = PortalGunTransformUtil.pitchFromLook(look);
|
||||||
|
this.entity.setPos(transformed.x, transformed.y, transformed.z);
|
||||||
|
this.entity.xo = transformedPrevious.x;
|
||||||
|
this.entity.yo = transformedPrevious.y;
|
||||||
|
this.entity.zo = transformedPrevious.z;
|
||||||
|
this.entity.setYRot(yaw);
|
||||||
|
this.entity.setXRot(pitch);
|
||||||
|
this.entity.yRotO = yaw;
|
||||||
|
this.entity.xRotO = pitch;
|
||||||
|
this.entity.setDeltaMovement(velocity);
|
||||||
|
if (this.entity instanceof LivingEntity living) {
|
||||||
|
living.setYHeadRot(yaw);
|
||||||
|
living.setYBodyRot(yaw);
|
||||||
|
}
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
while (!this.stack.isEmpty()) {
|
||||||
|
this.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record EntityTransformation(
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
double z,
|
||||||
|
double xo,
|
||||||
|
double yo,
|
||||||
|
double zo,
|
||||||
|
float yRot,
|
||||||
|
float xRot,
|
||||||
|
float yRotO,
|
||||||
|
float xRotO,
|
||||||
|
float yHeadRot,
|
||||||
|
float yBodyRot,
|
||||||
|
Vec3 deltaMovement
|
||||||
|
) {
|
||||||
|
private static EntityTransformation capture(Entity entity) {
|
||||||
|
float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot();
|
||||||
|
float yBodyRot = entity instanceof LivingEntity living ? living.yBodyRot : entity.getYRot();
|
||||||
|
return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yBodyRot, entity.getDeltaMovement());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restore(Entity entity) {
|
||||||
|
entity.setPos(this.x, this.y, this.z);
|
||||||
|
entity.xo = this.xo;
|
||||||
|
entity.yo = this.yo;
|
||||||
|
entity.zo = this.zo;
|
||||||
|
entity.setYRot(this.yRot);
|
||||||
|
entity.setXRot(this.xRot);
|
||||||
|
entity.yRotO = this.yRotO;
|
||||||
|
entity.xRotO = this.xRotO;
|
||||||
|
entity.setDeltaMovement(this.deltaMovement);
|
||||||
|
if (entity instanceof LivingEntity living) {
|
||||||
|
living.setYHeadRot(this.yHeadRot);
|
||||||
|
living.setYBodyRot(this.yBodyRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+139
@@ -0,0 +1,139 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
public final class PortalGunPortalRenderState {
|
||||||
|
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_DESTINATION_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
|
||||||
|
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_SOURCE_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
|
||||||
|
private static final ThreadLocal<Deque<PortalRenderContext>> ACTIVE_CONTEXTS = ThreadLocal.withInitial(ArrayDeque::new);
|
||||||
|
private static final ThreadLocal<Integer> RENDER_ALL_DEPTH = ThreadLocal.withInitial(() -> 0);
|
||||||
|
|
||||||
|
private PortalGunPortalRenderState() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pushPortalView(PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
|
||||||
|
ACTIVE_SOURCE_SHAPES.get().push(sourceShape);
|
||||||
|
ACTIVE_DESTINATION_SHAPES.get().push(destinationShape);
|
||||||
|
if (renderAll) {
|
||||||
|
RENDER_ALL_DEPTH.set(RENDER_ALL_DEPTH.get() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pushPortalView(PortalRenderContext context) {
|
||||||
|
ACTIVE_CONTEXTS.get().push(context);
|
||||||
|
pushPortalView(context.sourceShape(), context.destinationShape(), context.renderAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void popPortalView(boolean renderAll) {
|
||||||
|
Deque<PortalGunWorldPortalShape> sourceShapes = ACTIVE_SOURCE_SHAPES.get();
|
||||||
|
if (!sourceShapes.isEmpty()) {
|
||||||
|
sourceShapes.pop();
|
||||||
|
}
|
||||||
|
if (sourceShapes.isEmpty()) {
|
||||||
|
ACTIVE_SOURCE_SHAPES.remove();
|
||||||
|
}
|
||||||
|
Deque<PortalGunWorldPortalShape> destinationShapes = ACTIVE_DESTINATION_SHAPES.get();
|
||||||
|
if (!destinationShapes.isEmpty()) {
|
||||||
|
destinationShapes.pop();
|
||||||
|
}
|
||||||
|
if (destinationShapes.isEmpty()) {
|
||||||
|
ACTIVE_DESTINATION_SHAPES.remove();
|
||||||
|
}
|
||||||
|
if (renderAll) {
|
||||||
|
int depth = Math.max(0, RENDER_ALL_DEPTH.get() - 1);
|
||||||
|
if (depth == 0) {
|
||||||
|
RENDER_ALL_DEPTH.remove();
|
||||||
|
} else {
|
||||||
|
RENDER_ALL_DEPTH.set(depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Deque<PortalRenderContext> contexts = ACTIVE_CONTEXTS.get();
|
||||||
|
if (!contexts.isEmpty()) {
|
||||||
|
contexts.pop();
|
||||||
|
}
|
||||||
|
if (contexts.isEmpty()) {
|
||||||
|
ACTIVE_CONTEXTS.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pushDestinationShape(PortalGunWorldPortalShape shape) {
|
||||||
|
ACTIVE_DESTINATION_SHAPES.get().push(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void popDestinationShape() {
|
||||||
|
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
|
||||||
|
if (!shapes.isEmpty()) {
|
||||||
|
shapes.pop();
|
||||||
|
}
|
||||||
|
if (shapes.isEmpty()) {
|
||||||
|
ACTIVE_DESTINATION_SHAPES.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PortalGunWorldPortalShape getDestinationShape() {
|
||||||
|
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
|
||||||
|
return shapes.isEmpty() ? null : shapes.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PortalGunWorldPortalShape getSourceShape() {
|
||||||
|
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_SOURCE_SHAPES.get();
|
||||||
|
return shapes.isEmpty() ? null : shapes.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PortalRenderContext getContext() {
|
||||||
|
Deque<PortalRenderContext> contexts = ACTIVE_CONTEXTS.get();
|
||||||
|
return contexts.isEmpty() ? null : contexts.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRenderLevel() {
|
||||||
|
return ACTIVE_CONTEXTS.get().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean renderAll() {
|
||||||
|
return RENDER_ALL_DEPTH.get() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldRenderBounds(AABB bounds) {
|
||||||
|
if (renderAll()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PortalRenderContext context = getContext();
|
||||||
|
if (context == null) {
|
||||||
|
PortalGunWorldPortalShape destinationShape = getDestinationShape();
|
||||||
|
PortalGunWorldPortalShape sourceShape = getSourceShape();
|
||||||
|
return destinationShape == null || shouldRenderBounds(bounds, sourceShape, destinationShape, false);
|
||||||
|
}
|
||||||
|
return shouldRenderBounds(bounds, context.sourceShape(), context.destinationShape(), context.renderAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldRenderBounds(AABB bounds, PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
|
||||||
|
if (renderAll) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!destinationShape.intersectsFront(bounds, 0.125D)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!destinationShape.intersectsPortalColumn(bounds, 1.5D, 1.5D, destinationShape.halfDepth() + 96.0D)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return sourceShape == null || !sourceShape.intersectsFront(bounds, -0.02D);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PortalRenderContext(
|
||||||
|
PortalGunWorldPortalShape sourceShape,
|
||||||
|
PortalGunWorldPortalShape destinationShape,
|
||||||
|
Vec3 cameraPos,
|
||||||
|
Vec3 look,
|
||||||
|
Vec3 up,
|
||||||
|
Matrix4f viewMatrix,
|
||||||
|
Matrix4f projectionMatrix,
|
||||||
|
int renderLevel,
|
||||||
|
boolean renderAll
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public final class PortalGunRollClientState {
|
||||||
|
private static final int TELEPORT_COOLDOWN_TICKS = 40;
|
||||||
|
private static final float ROLL_DECAY = 0.85F;
|
||||||
|
private static final float ROLL_EPSILON = 0.05F;
|
||||||
|
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
|
||||||
|
private static float previousRollDegrees;
|
||||||
|
private static float rollDegrees;
|
||||||
|
|
||||||
|
private PortalGunRollClientState() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float getRollDegrees() {
|
||||||
|
return rollDegrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float getRollDegrees(float partialTick) {
|
||||||
|
return previousRollDegrees + (rollDegrees - previousRollDegrees) * partialTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEntityInsidePortal(Minecraft minecraft, Entity entity) {
|
||||||
|
if (minecraft.level == null || entity == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Entity renderEntity : minecraft.level.entitiesForRendering()) {
|
||||||
|
if (renderEntity instanceof PortalGunPortalEntity portal && portal.isAlive() && portal.intersectsEntityBounds(entity)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCameraInsidePortal(Minecraft minecraft) {
|
||||||
|
Entity cameraEntity = minecraft.getCameraEntity();
|
||||||
|
return cameraEntity != null && isEntityInsidePortal(minecraft, cameraEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
previousRollDegrees = 0.0F;
|
||||||
|
rollDegrees = 0.0F;
|
||||||
|
TELEPORT_COOLDOWNS.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick(Minecraft minecraft) {
|
||||||
|
if (minecraft.level == null || minecraft.player == null) {
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entity cameraEntity = minecraft.getCameraEntity();
|
||||||
|
if (cameraEntity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previousRollDegrees = rollDegrees;
|
||||||
|
rollDegrees *= ROLL_DECAY;
|
||||||
|
if (Math.abs(rollDegrees) < ROLL_EPSILON) {
|
||||||
|
rollDegrees = 0.0F;
|
||||||
|
}
|
||||||
|
long gameTime = minecraft.level.getGameTime();
|
||||||
|
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
|
||||||
|
for (Entity entity : minecraft.level.entitiesForRendering()) {
|
||||||
|
if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
|
||||||
|
if (linkedPortal == null || !linkedPortal.isAlive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!portal.shouldRenderFront(cameraEntity.position())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!canTeleport(cameraEntity, gameTime)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Vec3 crossingProbe = portal.resolveCrossingProbe(cameraEntity);
|
||||||
|
if (crossingProbe == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossingProbe, gameTime);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean canTeleport(Entity entity, long gameTime) {
|
||||||
|
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
|
||||||
|
return cooldownUntil <= gameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 currentProbe, long gameTime) {
|
||||||
|
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getLookAngle()).normalize();
|
||||||
|
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees)).normalize();
|
||||||
|
Vec3 relativeProbe = currentProbe.subtract(sourcePortal.position());
|
||||||
|
Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, relativeProbe);
|
||||||
|
Vec3 transformedEntityOffset = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.position().subtract(currentProbe));
|
||||||
|
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
|
||||||
|
double exitClearance = Math.max(0.35D, entity.getBbWidth() * 0.5D + 0.16D);
|
||||||
|
Vec3 exitProbe = destinationPortal.position().add(transformedProbe).add(destinationNormal.scale(exitClearance - transformedProbe.dot(destinationNormal)));
|
||||||
|
Vec3 exitPos = exitProbe.add(transformedEntityOffset);
|
||||||
|
Vec3 transformedMotion = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getDeltaMovement());
|
||||||
|
double outward = transformedMotion.dot(destinationNormal);
|
||||||
|
if (outward < 0.42D) {
|
||||||
|
transformedMotion = transformedMotion.add(destinationNormal.scale(0.42D - outward));
|
||||||
|
}
|
||||||
|
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
|
||||||
|
entity.teleportTo(exitPos.x, exitPos.y, exitPos.z);
|
||||||
|
entity.setDeltaMovement(transformedMotion);
|
||||||
|
entity.setYRot(PortalGunTransformUtil.yawFromLook(transformedLook));
|
||||||
|
entity.setXRot(PortalGunTransformUtil.pitchFromLook(transformedLook));
|
||||||
|
TELEPORT_COOLDOWNS.put(entity.getUUID(), gameTime + TELEPORT_COOLDOWN_TICKS);
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public final class WormHookTetherClientState {
|
||||||
|
private static final Map<Integer, Integer> TETHERS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private WormHookTetherClientState() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update(int playerId, int hookId) {
|
||||||
|
if (hookId < 0) {
|
||||||
|
TETHERS.remove(playerId);
|
||||||
|
} else {
|
||||||
|
TETHERS.put(playerId, hookId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, Integer> snapshot() {
|
||||||
|
return Map.copyOf(TETHERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
TETHERS.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class HoverboardModel extends GeoModel<HoverboardEntity> {
|
||||||
|
private static final ResourceLocation MODEL = new ResourceLocation(Antarchy.MODID, "geo/hoverboard.geo.json");
|
||||||
|
private static final ResourceLocation ANIMATION = new ResourceLocation(Antarchy.MODID, "animations/hoverboard.animation.json");
|
||||||
|
private static final ResourceLocation BASE_TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/entity/hoverboard/hoverboard.png");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(HoverboardEntity animatable) {
|
||||||
|
return MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(HoverboardEntity animatable) {
|
||||||
|
DyeColor color = animatable.getColor();
|
||||||
|
if (color == null) {
|
||||||
|
return BASE_TEXTURE;
|
||||||
|
}
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/hoverboard/hoverboard_" + color.getName() + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(HoverboardEntity animatable) {
|
||||||
|
return ANIMATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class ManticoreModel extends GeoModel<ManticoreEntity> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(ManticoreEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/manticore.geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(ManticoreEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/manticore.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(ManticoreEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/manticore.animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunBlackHoleEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class PortalGunBlackHoleModel extends GeoModel<PortalGunBlackHoleEntity> {
|
||||||
|
private static final ResourceLocation TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/vfx/portal_gun_black_hole.png");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(PortalGunBlackHoleEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/portal_gun_portal.geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(PortalGunBlackHoleEntity animatable) {
|
||||||
|
return TEXTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(PortalGunBlackHoleEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/portal_gun_portal.animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class PortalGunPortalModel extends GeoModel<PortalGunPortalEntity> {
|
||||||
|
private static final ResourceLocation BLUE_TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
|
||||||
|
private static final ResourceLocation ORANGE_TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(PortalGunPortalEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/portal_gun_portal.geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(PortalGunPortalEntity animatable) {
|
||||||
|
return animatable.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(PortalGunPortalEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/portal_gun_portal.animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class RoyalBlackHoleModel<T extends RoyalBlackHoleEntity> extends GeoModel<T> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(T animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/black_hole.geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(T animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/queen/black_hole.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(T animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/black_hole.animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class RoyalBossModel extends GeoModel<RoyalBossEntity> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(RoyalBossEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/" + animatable.geoNameForRender() + ".geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(RoyalBossEntity animatable) {
|
||||||
|
String name = animatable.geoNameForRender();
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/" + name + "/" + name + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(RoyalBossEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/" + animatable.geoNameForRender() + ".animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalElementalProjectileEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class RoyalElementalProjectileModel extends GeoModel<RoyalElementalProjectileEntity> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(RoyalElementalProjectileEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID,
|
||||||
|
"geo/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(RoyalElementalProjectileEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID,
|
||||||
|
"textures/entity/king/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(RoyalElementalProjectileEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID,
|
||||||
|
"animations/" + (animatable.isIceball() ? "ice_ball" : "fire_ball") + ".animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class RoyalIceSpikeModel extends GeoModel<RoyalIceSpikeEntity> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(RoyalIceSpikeEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/ice_spike.geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(RoyalIceSpikeEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/king/ice_spike.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(RoyalIceSpikeEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/ice_spike.animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class RoyalMountModel extends GeoModel<RoyalMountEntity> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(RoyalMountEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/" + animatable.geoNameForRender() + ".geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(RoyalMountEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/" + animatable.geoNameForRender() + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(RoyalMountEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/" + animatable.geoNameForRender() + ".animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.vortex.VortexEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class VortexModel extends GeoModel<VortexEntity> {
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(VortexEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "geo/vortex.geo.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(VortexEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "textures/entity/vortex.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(VortexEntity animatable) {
|
||||||
|
return new ResourceLocation(Antarchy.MODID, "animations/vortex.animation.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.model;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.entity.WormEntity;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import software.bernie.geckolib.model.GeoModel;
|
||||||
|
|
||||||
|
public class WormModel extends GeoModel<WormEntity> {
|
||||||
|
private static final ResourceLocation MODEL = new ResourceLocation(Antarchy.MODID, "geo/worm_large.geo.json");
|
||||||
|
private static final ResourceLocation TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/entity/worm/worm_large.png");
|
||||||
|
private static final ResourceLocation ANIMATION = new ResourceLocation(Antarchy.MODID, "animations/worm_large.animation.json");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getModelResource(WormEntity animatable) {
|
||||||
|
return MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureResource(WormEntity animatable) {
|
||||||
|
return TEXTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getAnimationResource(WormEntity animatable) {
|
||||||
|
return ANIMATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.HoverboardModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class HoverboardRenderer extends GeoEntityRenderer<HoverboardEntity> {
|
||||||
|
public HoverboardRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new HoverboardModel());
|
||||||
|
this.shadowRadius = 0.5F;
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.ManticoreModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class ManticoreRenderer extends GeoEntityRenderer<ManticoreEntity> {
|
||||||
|
public ManticoreRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new ManticoreModel());
|
||||||
|
this.shadowRadius = 0.9F;
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -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 = new ResourceLocation("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, 1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.PortalGunBlackHoleModel;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunBlackHoleEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class PortalGunBlackHoleRenderer extends GeoEntityRenderer<PortalGunBlackHoleEntity> {
|
||||||
|
public PortalGunBlackHoleRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new PortalGunBlackHoleModel());
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PortalGunBlackHoleEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable RenderType getRenderType(PortalGunBlackHoleEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||||
|
return RenderType.entityTranslucent(this.getGeoModel().getTextureResource(animatable));
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.PortalGunPortalModel;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEntity> {
|
||||||
|
public PortalGunPortalRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new PortalGunPortalModel());
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PortalGunPortalEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
poseStack.pushPose();
|
||||||
|
VertexConsumer consumer = bufferSource.getBuffer(RenderType.entityTranslucent(this.getGeoModel().getTextureResource(entity)));
|
||||||
|
Matrix4f pose = poseStack.last().pose();
|
||||||
|
putPortalVertex(consumer, pose, entity, -0.5F, -1.0F, 0.0F, 1.0F, false);
|
||||||
|
putPortalVertex(consumer, pose, entity, -0.5F, 1.0F, 0.0F, 0.0F, false);
|
||||||
|
putPortalVertex(consumer, pose, entity, 0.5F, 1.0F, 1.0F, 0.0F, false);
|
||||||
|
putPortalVertex(consumer, pose, entity, 0.5F, -1.0F, 1.0F, 1.0F, false);
|
||||||
|
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(this.getGeoModel().getTextureResource(entity)));
|
||||||
|
putPortalVertex(edge, pose, entity, -0.56F, -1.07F, 0.0F, 1.0F, true);
|
||||||
|
putPortalVertex(edge, pose, entity, -0.56F, 1.07F, 0.0F, 0.0F, true);
|
||||||
|
putPortalVertex(edge, pose, entity, 0.56F, 1.07F, 1.0F, 0.0F, true);
|
||||||
|
putPortalVertex(edge, pose, entity, 0.56F, -1.07F, 1.0F, 1.0F, true);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void putPortalVertex(VertexConsumer consumer, Matrix4f pose, PortalGunPortalEntity entity, float horizontal, float vertical, float u, float v, boolean channelTint) {
|
||||||
|
Vec3 position = entity.getWidthVec().normalize().scale(horizontal)
|
||||||
|
.add(entity.getUpVec().normalize().scale(vertical))
|
||||||
|
.add(entity.getNormalVec().normalize().scale(0.03125D));
|
||||||
|
int red = channelTint ? entity.getChannelRed() : 255;
|
||||||
|
int green = channelTint ? entity.getChannelGreen() : 255;
|
||||||
|
int blue = channelTint ? entity.getChannelBlue() : 255;
|
||||||
|
int alpha = channelTint ? 150 : 210;
|
||||||
|
consumer.vertex(pose, (float) position.x, (float) position.y, (float) position.z)
|
||||||
|
.color(red, green, blue, alpha)
|
||||||
|
.uv(u, v)
|
||||||
|
.overlayCoords(OverlayTexture.NO_OVERLAY)
|
||||||
|
.uv2(LightTexture.FULL_BRIGHT)
|
||||||
|
.normal((float) entity.getNormalVec().x, (float) entity.getNormalVec().y, (float) entity.getNormalVec().z)
|
||||||
|
.endVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void applyRotations(PortalGunPortalEntity animatable, PoseStack poseStack, float ageInTicks, float rotationYaw, float partialTick, float nativeScale) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable RenderType getRenderType(PortalGunPortalEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||||
|
return RenderType.entityTranslucent(this.getGeoModel().getTextureResource(animatable));
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import net.minecraft.client.Camera;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
/** 1.20 hook for the optional recursive portal framebuffer path. */
|
||||||
|
public final class PortalGunPortalViewRenderer {
|
||||||
|
private PortalGunPortalViewRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void render(Camera camera, Matrix4f poseMatrix, float tickCounter) {
|
||||||
|
// Disabled on 1.20 until its LevelRenderer camera contract is ported.
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
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.EntityRendererProvider;
|
||||||
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProjectileEntity> {
|
||||||
|
private static final ResourceLocation BLUE_TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
|
||||||
|
private static final ResourceLocation ORANGE_TEXTURE = new ResourceLocation(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
|
||||||
|
|
||||||
|
public PortalGunProjectileRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PortalGunProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
|
||||||
|
float age = entity.tickCount + partialTick;
|
||||||
|
float syncedDistance = entity.getSyncedDistance() + partialTick;
|
||||||
|
float pulse = 0.8F + 0.2F * (float) Math.sin(age * 0.9F);
|
||||||
|
float travelPulse = 0.82F + 0.18F * (float) Math.sin(syncedDistance * 0.55F);
|
||||||
|
float trailStretch = 1.1F + (float) Math.min(entity.getSyncedVelocity().length() * 0.42D, 1.8D);
|
||||||
|
float baseScale = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.44F : 0.48F;
|
||||||
|
ResourceLocation texture = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
|
||||||
|
float red = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.55F : 1.0F;
|
||||||
|
float green = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.82F : 0.56F;
|
||||||
|
float blue = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 1.0F : 0.18F;
|
||||||
|
|
||||||
|
poseStack.scale(baseScale * trailStretch, baseScale * travelPulse, baseScale * travelPulse);
|
||||||
|
PoseStack.Pose pose = poseStack.last();
|
||||||
|
Matrix4f matrix = pose.pose();
|
||||||
|
VertexConsumer core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
|
||||||
|
addQuad(core, pose, matrix, red, green, blue, 0.92F, packedLight, -0.9F, -0.5F, 0.9F, 0.5F);
|
||||||
|
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
|
||||||
|
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.8F * pulse, 0xF000F0, -1.15F, -0.7F, 1.15F, 0.7F);
|
||||||
|
|
||||||
|
poseStack.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(90.0F));
|
||||||
|
pose = poseStack.last();
|
||||||
|
matrix = pose.pose();
|
||||||
|
core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
|
||||||
|
addQuad(core, pose, matrix, red, green, blue, 0.74F, packedLight, -0.45F, -0.95F, 0.45F, 0.95F);
|
||||||
|
glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
|
||||||
|
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.52F * pulse, 0xF000F0, -0.62F, -1.15F, 0.62F, 1.15F);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getBlockLightLevel(PortalGunProjectileEntity entity, BlockPos pos) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureLocation(PortalGunProjectileEntity entity) {
|
||||||
|
return entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addQuad(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float red, float green, float blue, float alpha, int packedLight, float minX, float minY, float maxX, float maxY) {
|
||||||
|
addVertex(consumer, pose, matrix, minX, minY, 0.0F, 1.0F, red, green, blue, alpha, packedLight);
|
||||||
|
addVertex(consumer, pose, matrix, maxX, minY, 1.0F, 1.0F, red, green, blue, alpha, packedLight);
|
||||||
|
addVertex(consumer, pose, matrix, maxX, maxY, 1.0F, 0.0F, red, green, blue, alpha, packedLight);
|
||||||
|
addVertex(consumer, pose, matrix, minX, maxY, 0.0F, 0.0F, red, green, blue, alpha, packedLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addVertex(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float x, float y, float u, float v, float red, float green, float blue, float alpha, int packedLight) {
|
||||||
|
consumer.vertex(matrix, x, y, 0.0F)
|
||||||
|
.color((int) (red * 255.0F), (int) (green * 255.0F), (int) (blue * 255.0F), (int) (alpha * 255.0F))
|
||||||
|
.uv(u, v)
|
||||||
|
.overlayCoords(OverlayTexture.NO_OVERLAY)
|
||||||
|
.uv2(packedLight)
|
||||||
|
.normal(pose.normal(), 0.0F, 0.0F, 1.0F)
|
||||||
|
.endVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.item.RoyalAssailantBattleAxeItem;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.model.PlayerModel;
|
||||||
|
import net.minecraft.client.player.AbstractClientPlayer;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.RenderLayerParent;
|
||||||
|
import net.minecraft.client.renderer.entity.layers.RenderLayer;
|
||||||
|
import net.minecraft.world.entity.HumanoidArm;
|
||||||
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
public final class RoyalAssailantAxeAfterimageLayer extends RenderLayer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
|
||||||
|
private static final int[] TINTS = {0x66FF3B4D, 0x4DFF263D, 0x33E8142F, 0x1FCC0D26};
|
||||||
|
|
||||||
|
public RoyalAssailantAxeAfterimageLayer(RenderLayerParent<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> renderer) {
|
||||||
|
super(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack poseStack, MultiBufferSource buffer, int packedLight, AbstractClientPlayer player,
|
||||||
|
float limbSwing, float limbSwingAmount, float partialTick, float ageInTicks,
|
||||||
|
float netHeadYaw, float headPitch) {
|
||||||
|
if (player.isSpectator() || player.isInvisible() || player.getAttackAnim(partialTick) <= 0.01F) return;
|
||||||
|
ItemStack stack = player.getMainHandItem();
|
||||||
|
HumanoidArm arm = player.getMainArm();
|
||||||
|
if (!(stack.getItem() instanceof RoyalAssailantBattleAxeItem)) {
|
||||||
|
stack = player.getOffhandItem();
|
||||||
|
arm = player.getMainArm() == HumanoidArm.RIGHT ? HumanoidArm.LEFT : HumanoidArm.RIGHT;
|
||||||
|
}
|
||||||
|
if (!(stack.getItem() instanceof RoyalAssailantBattleAxeItem)) return;
|
||||||
|
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(player)));
|
||||||
|
float swing = player.getAttackAnim(partialTick);
|
||||||
|
for (int i = TINTS.length - 1; i >= 0; i--) {
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(0.0D, 0.0D, (i + 1) * 0.08F);
|
||||||
|
poseStack.mulPose(Axis.YP.rotation((i + 1) * 0.12F * (swing < 0.5F ? -1.0F : 1.0F)));
|
||||||
|
int tint = TINTS[i];
|
||||||
|
this.getParentModel().renderToBuffer(poseStack, vertexConsumer, LightTexture.FULL_BRIGHT,
|
||||||
|
LivingEntityRenderer.getOverlayCoords(player, 0.0F),
|
||||||
|
((tint >> 16) & 0xFF) / 255.0F, ((tint >> 8) & 0xFF) / 255.0F,
|
||||||
|
(tint & 0xFF) / 255.0F, ((tint >>> 24) & 0xFF) / 255.0F);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
for (int i = TINTS.length - 1; i >= 0; i--) {
|
||||||
|
poseStack.pushPose();
|
||||||
|
this.getParentModel().translateToHand(arm, poseStack);
|
||||||
|
poseStack.mulPose(Axis.YP.rotation((i + 1) * 0.12F * (swing < 0.5F ? -1.0F : 1.0F)));
|
||||||
|
Minecraft.getInstance().getItemRenderer().renderStatic(player, stack,
|
||||||
|
arm == HumanoidArm.RIGHT ? ItemDisplayContext.THIRD_PERSON_RIGHT_HAND : ItemDisplayContext.THIRD_PERSON_LEFT_HAND,
|
||||||
|
arm == HumanoidArm.LEFT, poseStack, buffer, player.level(), packedLight,
|
||||||
|
LivingEntityRenderer.getOverlayCoords(player, 0.0F), player.getId());
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.RoyalBlackHoleModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalAssailantBlackHoleEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public final class RoyalAssailantBlackHoleRenderer extends GeoEntityRenderer<RoyalAssailantBlackHoleEntity> {
|
||||||
|
public RoyalAssailantBlackHoleRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new RoyalBlackHoleModel<>());
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RoyalAssailantBlackHoleEntity entity, float entityYaw, float partialTick,
|
||||||
|
PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable RenderType getRenderType(RoyalAssailantBlackHoleEntity animatable, ResourceLocation texture,
|
||||||
|
@Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||||
|
return RenderType.entityTranslucent(this.getGeoModel().getTextureResource(animatable));
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.RoyalBlackHoleModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class RoyalBlackHoleRenderer extends GeoEntityRenderer<RoyalBlackHoleEntity> {
|
||||||
|
public RoyalBlackHoleRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new RoyalBlackHoleModel<>());
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RoyalBlackHoleEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
|
||||||
|
MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable RenderType getRenderType(RoyalBlackHoleEntity animatable, ResourceLocation texture,
|
||||||
|
@Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||||
|
return RenderType.entityTranslucent(this.getGeoModel().getTextureResource(animatable));
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalBoltEntity;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public class RoyalBoltRenderer extends EntityRenderer<RoyalBoltEntity> {
|
||||||
|
private static final ResourceLocation TEXTURE = new ResourceLocation("minecraft", "textures/particle/generic_0.png");
|
||||||
|
|
||||||
|
public RoyalBoltRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureLocation(RoyalBoltEntity entity) {
|
||||||
|
return TEXTURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.RoyalBossModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
import software.bernie.geckolib.core.object.Color;
|
||||||
|
|
||||||
|
public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
|
||||||
|
private static final ResourceLocation QUEEN_BEAM_OUTER = new ResourceLocation("antarchy", "textures/entity/queen/queen_beam_outer.png");
|
||||||
|
private static final ResourceLocation QUEEN_BEAM_INNER = new ResourceLocation("antarchy", "textures/entity/queen/queen_beam_inner.png");
|
||||||
|
private static final ResourceLocation QUEEN_BEAM_END = new ResourceLocation("antarchy", "textures/entity/queen/queen_beam_end_1.png");
|
||||||
|
private static final ResourceLocation KING_FIRE_BEAM_OUTER = new ResourceLocation("antarchy", "textures/entity/king/fire_beam_outer.png");
|
||||||
|
private static final ResourceLocation KING_FIRE_BEAM_INNER = new ResourceLocation("antarchy", "textures/entity/king/fire_beam_inner.png");
|
||||||
|
private static final ResourceLocation KING_FIRE_BEAM_END = new ResourceLocation("antarchy", "textures/entity/king/fire_beam_end_1.png");
|
||||||
|
private static final ResourceLocation KING_LIGHTNING_BEAM_OUTER = new ResourceLocation("antarchy", "textures/entity/king/lightning_beam_outer.png");
|
||||||
|
private static final ResourceLocation KING_LIGHTNING_BEAM_INNER = new ResourceLocation("antarchy", "textures/entity/king/lightning_beam_inner.png");
|
||||||
|
private static final ResourceLocation KING_LIGHTNING_BEAM_END = new ResourceLocation("antarchy", "textures/entity/king/lightning_beam_end_1.png");
|
||||||
|
private static final ResourceLocation KING_ICE_BEAM_OUTER = new ResourceLocation("antarchy", "textures/entity/king/ice_beam_outer.png");
|
||||||
|
private static final ResourceLocation KING_ICE_BEAM_INNER = new ResourceLocation("antarchy", "textures/entity/king/ice_beam_inner.png");
|
||||||
|
private static final ResourceLocation KING_ICE_BEAM_END = new ResourceLocation("antarchy", "textures/entity/king/ice_beam_end_1.png");
|
||||||
|
public RoyalBossRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new RoyalBossModel());
|
||||||
|
this.shadowRadius = 8.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderType getRenderType(RoyalBossEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||||
|
return RenderType.entityCutoutNoCull(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getRenderColor(RoyalBossEntity animatable, float partialTick, int packedLight) {
|
||||||
|
if (!animatable.isRoyalAccelerated()) {
|
||||||
|
return Color.ofRGBA(255, 255, 255, 255);
|
||||||
|
}
|
||||||
|
float pulse = 0.5F + 0.5F * (float) Math.sin((animatable.tickCount + partialTick) * 0.4F);
|
||||||
|
int green = 60 + Math.round(60.0F * pulse);
|
||||||
|
return Color.ofRGBA(255, green, green, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRender(PoseStack poseStack, RoyalBossEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource,
|
||||||
|
@Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay,
|
||||||
|
float red, float green, float blue, float alpha) {
|
||||||
|
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, red, green, blue, alpha);
|
||||||
|
poseStack.scale(RoyalBossEntity.MODEL_RENDER_SCALE, RoyalBossEntity.MODEL_RENDER_SCALE, RoyalBossEntity.MODEL_RENDER_SCALE);
|
||||||
|
this.shadowRadius = 8.0F * RoyalBossEntity.MODEL_RENDER_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final float BEAM_OUTER_RADIUS = 0.55F;
|
||||||
|
private static final float BEAM_INNER_RADIUS = 0.32F;
|
||||||
|
private static final float BEAM_TILE_BLOCKS = 3.0F;
|
||||||
|
private static final float BEAM_SCROLL_SPEED = 0.32F;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
|
||||||
|
MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
|
||||||
|
Vec3 end = entity.getRoyalBeamEndPosition();
|
||||||
|
if (!entity.isFiringRoyalBeam() || end == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double x = Mth.lerp(partialTick, entity.xo, entity.getX());
|
||||||
|
double y = Mth.lerp(partialTick, entity.yo, entity.getY());
|
||||||
|
double z = Mth.lerp(partialTick, entity.zo, entity.getZ());
|
||||||
|
Vec3 start = entity.getRoyalBeamShootFrom(partialTick).subtract(x, y, z);
|
||||||
|
Vec3 finish = end.subtract(x, y, z);
|
||||||
|
Vec3 axis = finish.subtract(start);
|
||||||
|
double length = axis.length();
|
||||||
|
if (length < 0.05D) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vec3 dir = axis.scale(1.0D / length);
|
||||||
|
Vec3 side1 = dir.cross(new Vec3(0.0D, 1.0D, 0.0D));
|
||||||
|
if (side1.lengthSqr() < 1.0E-4D) {
|
||||||
|
side1 = dir.cross(new Vec3(1.0D, 0.0D, 0.0D));
|
||||||
|
}
|
||||||
|
side1 = side1.normalize();
|
||||||
|
Vec3 side2 = dir.cross(side1).normalize();
|
||||||
|
|
||||||
|
ResourceLocation outer = QUEEN_BEAM_OUTER;
|
||||||
|
ResourceLocation inner = QUEEN_BEAM_INNER;
|
||||||
|
ResourceLocation endTexture = QUEEN_BEAM_END;
|
||||||
|
if (entity instanceof KingEntity) {
|
||||||
|
switch (entity.getRoyalBeamElement()) {
|
||||||
|
case FIRE -> {
|
||||||
|
outer = KING_FIRE_BEAM_OUTER;
|
||||||
|
inner = KING_FIRE_BEAM_INNER;
|
||||||
|
endTexture = KING_FIRE_BEAM_END;
|
||||||
|
}
|
||||||
|
case LIGHTNING -> {
|
||||||
|
outer = KING_LIGHTNING_BEAM_OUTER;
|
||||||
|
inner = KING_LIGHTNING_BEAM_INNER;
|
||||||
|
endTexture = KING_LIGHTNING_BEAM_END;
|
||||||
|
}
|
||||||
|
case ICE -> {
|
||||||
|
outer = KING_ICE_BEAM_OUTER;
|
||||||
|
inner = KING_ICE_BEAM_INNER;
|
||||||
|
endTexture = KING_ICE_BEAM_END;
|
||||||
|
}
|
||||||
|
case GENERIC -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float time = entity.tickCount + partialTick;
|
||||||
|
float tiles = (float) (length / BEAM_TILE_BLOCKS);
|
||||||
|
float scroll = -time * BEAM_SCROLL_SPEED;
|
||||||
|
var pose = poseStack.last().pose();
|
||||||
|
|
||||||
|
VertexConsumer outerBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(outer));
|
||||||
|
drawBeamPlane(outerBuffer, pose, start, finish, side1.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles);
|
||||||
|
drawBeamPlane(outerBuffer, pose, start, finish, side2.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles);
|
||||||
|
|
||||||
|
float innerScroll = -time * BEAM_SCROLL_SPEED * 1.6F;
|
||||||
|
VertexConsumer innerBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(inner));
|
||||||
|
drawBeamPlane(innerBuffer, pose, start, finish, side1.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles);
|
||||||
|
drawBeamPlane(innerBuffer, pose, start, finish, side2.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles);
|
||||||
|
|
||||||
|
float impactSize = 1.1F + 0.35F * Mth.sin(time * 0.6F);
|
||||||
|
Vec3 camLeft = new Vec3(this.entityRenderDispatcher.camera.getLeftVector());
|
||||||
|
Vec3 camUp = new Vec3(this.entityRenderDispatcher.camera.getUpVector());
|
||||||
|
VertexConsumer endBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(endTexture));
|
||||||
|
drawBillboard(endBuffer, pose, finish, camLeft.scale(impactSize), camUp.scale(impactSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawBeamPlane(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end, Vec3 half,
|
||||||
|
float startV, float endV) {
|
||||||
|
emitQuad(vertices, pose,
|
||||||
|
start.subtract(half), start.add(half), end.add(half), end.subtract(half),
|
||||||
|
0.0F, startV, 1.0F, endV);
|
||||||
|
emitQuad(vertices, pose,
|
||||||
|
start.add(half), start.subtract(half), end.subtract(half), end.add(half),
|
||||||
|
0.0F, startV, 1.0F, endV);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawBillboard(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 left, Vec3 up) {
|
||||||
|
emitQuad(vertices, pose,
|
||||||
|
center.add(left).add(up), center.subtract(left).add(up),
|
||||||
|
center.subtract(left).subtract(up), center.add(left).subtract(up),
|
||||||
|
0.0F, 0.0F, 1.0F, 1.0F);
|
||||||
|
emitQuad(vertices, pose,
|
||||||
|
center.subtract(left).add(up), center.add(left).add(up),
|
||||||
|
center.add(left).subtract(up), center.subtract(left).subtract(up),
|
||||||
|
0.0F, 0.0F, 1.0F, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void emitQuad(VertexConsumer vertices, org.joml.Matrix4f pose,
|
||||||
|
Vec3 a, Vec3 b, Vec3 c, Vec3 d,
|
||||||
|
float minU, float minV, float maxU, float maxV) {
|
||||||
|
vertex(vertices, pose, a, minU, minV);
|
||||||
|
vertex(vertices, pose, b, maxU, minV);
|
||||||
|
vertex(vertices, pose, c, maxU, maxV);
|
||||||
|
vertex(vertices, pose, d, minU, maxV);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v) {
|
||||||
|
vertices.vertex(pose, (float) pos.x, (float) pos.y, (float) pos.z)
|
||||||
|
.color(255, 255, 255, 255).uv(u, v)
|
||||||
|
.overlayCoords(net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY)
|
||||||
|
.uv2(0x00F000F0)
|
||||||
|
.normal(0.0F, 1.0F, 0.0F).endVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.RoyalElementalProjectileModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalElementalProjectileEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class RoyalElementalProjectileRenderer extends GeoEntityRenderer<RoyalElementalProjectileEntity> {
|
||||||
|
public RoyalElementalProjectileRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new RoyalElementalProjectileModel());
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRender(PoseStack poseStack, RoyalElementalProjectileEntity animatable, BakedGeoModel model,
|
||||||
|
MultiBufferSource bufferSource, VertexConsumer buffer, boolean isReRender,
|
||||||
|
float partialTick, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) {
|
||||||
|
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick,
|
||||||
|
packedLight, packedOverlay, red, green, blue, alpha);
|
||||||
|
float scale = animatable.isIceball() ? 0.75F : 0.36F;
|
||||||
|
poseStack.scale(scale, scale, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void applyRotations(RoyalElementalProjectileEntity entity, PoseStack poseStack, float ageInTicks,
|
||||||
|
float rotationYaw, float partialTick, float nativeScale) {
|
||||||
|
float yaw = Mth.rotLerp(partialTick, entity.yRotO, entity.getYRot());
|
||||||
|
float pitch = Mth.lerp(partialTick, entity.xRotO, entity.getXRot());
|
||||||
|
poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - yaw));
|
||||||
|
poseStack.mulPose(Axis.XP.rotationDegrees(-pitch));
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.RoyalIceSpikeModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class RoyalIceSpikeRenderer extends GeoEntityRenderer<RoyalIceSpikeEntity> {
|
||||||
|
public RoyalIceSpikeRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new RoyalIceSpikeModel());
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.RoyalMountModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalMountEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class RoyalMountRenderer extends GeoEntityRenderer<RoyalMountEntity> {
|
||||||
|
public RoyalMountRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new RoyalMountModel());
|
||||||
|
this.shadowRadius = 1.1F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderType getRenderType(RoyalMountEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||||
|
return RenderType.entityCutoutNoCull(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRender(PoseStack poseStack, RoyalMountEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource,
|
||||||
|
@Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay,
|
||||||
|
float red, float green, float blue, float alpha) {
|
||||||
|
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, red, green, blue, alpha);
|
||||||
|
float scale = animatable.getAgeScale();
|
||||||
|
poseStack.scale(scale, scale, scale);
|
||||||
|
this.shadowRadius = 1.1F * scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.ContractionAfterimages;
|
||||||
|
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import java.util.Deque;
|
||||||
|
import net.minecraft.client.model.EntityModel;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.RenderLayerParent;
|
||||||
|
import net.minecraft.client.renderer.entity.layers.RenderLayer;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public final class TemporalContractionAfterimageLayer<T extends LivingEntity, M extends EntityModel<T>> extends RenderLayer<T, M> {
|
||||||
|
private static final int RGB = 0x8FE6B4;
|
||||||
|
private static final float BASE_ALPHA = 0.42F;
|
||||||
|
|
||||||
|
public TemporalContractionAfterimageLayer(RenderLayerParent<T, M> renderer) { super(renderer); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack poseStack, MultiBufferSource buffer, int packedLight, T entity,
|
||||||
|
float limbSwing, float limbSwingAmount, float partialTick, float ageInTicks,
|
||||||
|
float netHeadYaw, float headPitch) {
|
||||||
|
if (entity.isSpectator() || entity.isInvisible() || !entity.hasEffect(RoyalEffectHooks.contractedHolder())) return;
|
||||||
|
Deque<ContractionAfterimages.Sample> samples = ContractionAfterimages.samples(entity);
|
||||||
|
if (samples == null || samples.isEmpty()) return;
|
||||||
|
double curX = Mth.lerp(partialTick, entity.xo, entity.getX());
|
||||||
|
double curY = Mth.lerp(partialTick, entity.yo, entity.getY());
|
||||||
|
double curZ = Mth.lerp(partialTick, entity.zo, entity.getZ());
|
||||||
|
int now = (int) (entity.level().getGameTime() & 0x7fffffffL);
|
||||||
|
Matrix4f inverse = new Matrix4f(poseStack.last().pose()).invert();
|
||||||
|
M model = this.getParentModel();
|
||||||
|
VertexConsumer consumer = buffer.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity)));
|
||||||
|
int index = 0;
|
||||||
|
for (ContractionAfterimages.Sample sample : samples) {
|
||||||
|
if (index++ % 2 != 0) continue;
|
||||||
|
double dx = sample.x - curX, dy = sample.y - curY, dz = sample.z - curZ;
|
||||||
|
if (dx * dx + dy * dy + dz * dz < 0.0025D) continue;
|
||||||
|
float fade = ContractionAfterimages.fade(sample, now);
|
||||||
|
if (fade <= 0.02F) continue;
|
||||||
|
Vector3f local = inverse.transformDirection(new Vector3f((float) dx, (float) dy, (float) dz));
|
||||||
|
int color = ((int) (BASE_ALPHA * fade * 255.0F) & 0xFF) << 24 | RGB;
|
||||||
|
model.setupAnim(entity, sample.limbPos, sample.limbSpeed, sample.ageInTicks,
|
||||||
|
sample.headYaw - sample.bodyYaw, sample.xRot);
|
||||||
|
poseStack.pushPose(); poseStack.translate(local.x, local.y, local.z);
|
||||||
|
model.renderToBuffer(poseStack, consumer, LightTexture.FULL_BRIGHT,
|
||||||
|
LivingEntityRenderer.getOverlayCoords(entity, 0.0F),
|
||||||
|
((color >> 16) & 0xFF) / 255.0F, ((color >> 8) & 0xFF) / 255.0F,
|
||||||
|
(color & 0xFF) / 255.0F, ((color >>> 24) & 0xFF) / 255.0F);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
model.setupAnim(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.ContractionAfterimages;
|
||||||
|
import com.craisinlord.antarchy.content.effect.RoyalEffectHooks;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import java.util.Deque;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.core.animatable.GeoAnimatable;
|
||||||
|
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||||
|
import software.bernie.geckolib.renderer.GeoRenderer;
|
||||||
|
import software.bernie.geckolib.renderer.layer.GeoRenderLayer;
|
||||||
|
|
||||||
|
public final class TemporalContractionGeoLayer<T extends GeoAnimatable> extends GeoRenderLayer<T> {
|
||||||
|
public TemporalContractionGeoLayer(GeoRenderer<T> renderer) { super(renderer); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack poseStack, T animatable, BakedGeoModel bakedModel, @Nullable RenderType renderType,
|
||||||
|
MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, float partialTick,
|
||||||
|
int packedLight, int packedOverlay) {
|
||||||
|
if (!(animatable instanceof Entity entity) || !(entity instanceof LivingEntity living)
|
||||||
|
|| living.isInvisible() || !living.hasEffect(RoyalEffectHooks.contractedHolder())) return;
|
||||||
|
Deque<ContractionAfterimages.Sample> samples = ContractionAfterimages.samples(living);
|
||||||
|
if (samples == null || samples.isEmpty()) return;
|
||||||
|
double curX = Mth.lerp(partialTick, entity.xo, entity.getX());
|
||||||
|
double curY = Mth.lerp(partialTick, entity.yo, entity.getY());
|
||||||
|
double curZ = Mth.lerp(partialTick, entity.zo, entity.getZ());
|
||||||
|
int now = (int) (entity.level().getGameTime() & 0x7fffffffL);
|
||||||
|
Matrix4f inverse = new Matrix4f(poseStack.last().pose()).invert();
|
||||||
|
RenderType type = RenderType.entityTranslucent(this.getRenderer().getTextureLocation(animatable));
|
||||||
|
VertexConsumer consumer = bufferSource.getBuffer(type);
|
||||||
|
int index = 0;
|
||||||
|
for (ContractionAfterimages.Sample sample : samples) {
|
||||||
|
if (index++ % 2 != 0) continue;
|
||||||
|
double dx = sample.x - curX, dy = sample.y - curY, dz = sample.z - curZ;
|
||||||
|
if (dx * dx + dy * dy + dz * dz < 0.0025D) continue;
|
||||||
|
float fade = ContractionAfterimages.fade(sample, now);
|
||||||
|
if (fade <= 0.02F) continue;
|
||||||
|
Vector3f local = inverse.transformDirection(new Vector3f((float) dx, (float) dy, (float) dz));
|
||||||
|
int color = ((int) (0.42F * fade * 255.0F) & 0xFF) << 24 | 0x8FE6B4;
|
||||||
|
poseStack.pushPose(); poseStack.translate(local.x, local.y, local.z);
|
||||||
|
this.getRenderer().reRender(bakedModel, poseStack, bufferSource, animatable, type, consumer,
|
||||||
|
partialTick, 0xF000F0, packedOverlay,
|
||||||
|
((color >> 16) & 0xFF) / 255.0F, ((color >> 8) & 0xFF) / 255.0F,
|
||||||
|
(color & 0xFF) / 255.0F, ((color >>> 24) & 0xFF) / 255.0F);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+259
@@ -0,0 +1,259 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
|
||||||
|
import com.mojang.blaze3d.platform.GlStateManager;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
|
import com.mojang.blaze3d.vertex.BufferUploader;
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexSorting;
|
||||||
|
import java.util.Random;
|
||||||
|
import net.minecraft.client.Camera;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
public final class ThoraxisUndersideSkyRenderer {
|
||||||
|
public static final ResourceKey<Level> THORAXIS = ResourceKey.create(
|
||||||
|
Registries.DIMENSION,
|
||||||
|
new ResourceLocation(Antarchy.MODID, "thoraxis")
|
||||||
|
);
|
||||||
|
private static final ResourceLocation SUN_TEXTURE = new ResourceLocation("minecraft", "textures/environment/sun.png");
|
||||||
|
private static final ResourceLocation MOON_TEXTURE = new ResourceLocation("minecraft", "textures/environment/moon_phases.png");
|
||||||
|
private static final float SKY_RADIUS = 100.0F;
|
||||||
|
private static final float BODY_SIZE = 32.0F;
|
||||||
|
private static final float SKY_RED = 0.015F;
|
||||||
|
private static final float SKY_GREEN = 0.0F;
|
||||||
|
private static final float SKY_BLUE = 0.035F;
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.Nullable
|
||||||
|
private static VertexBuffer redStarBuffer = null;
|
||||||
|
|
||||||
|
private ThoraxisUndersideSkyRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldRender(ClientLevel level, Camera camera) {
|
||||||
|
return level.dimension().equals(THORAXIS) && camera.getPosition().y < ThoraxisUndersideManager.GRAVITY_FLIP_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void render(
|
||||||
|
Minecraft minecraft,
|
||||||
|
PoseStack poseStack,
|
||||||
|
Matrix4f projectionMatrix,
|
||||||
|
float partialTick,
|
||||||
|
Camera camera,
|
||||||
|
boolean isFoggy,
|
||||||
|
Runnable setupFog,
|
||||||
|
VertexBuffer skyBuffer
|
||||||
|
) {
|
||||||
|
setupFog.run();
|
||||||
|
if (minecraft.options.hideLightningFlash().get() || minecraft.gameRenderer.getMainCamera() != camera) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderSystem.depthMask(false);
|
||||||
|
RenderSystem.setShaderColor(SKY_RED, SKY_GREEN, SKY_BLUE, 1.0F);
|
||||||
|
skyBuffer.bind();
|
||||||
|
skyBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionShader());
|
||||||
|
VertexBuffer.unbind();
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
|
||||||
|
float timeOfDay = 0.72F + partialTick * 0.0F;
|
||||||
|
renderCelestialBodies(poseStack.last().pose(), projectionMatrix, timeOfDay);
|
||||||
|
renderRedStars(poseStack.last().pose(), projectionMatrix);
|
||||||
|
|
||||||
|
RenderSystem.depthMask(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderRedStars(Matrix4f modelViewMatrix, Matrix4f projectionMatrix) {
|
||||||
|
if (redStarBuffer == null) {
|
||||||
|
redStarBuffer = buildRedStarBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.setShaderColor(0.95F, 0.95F, 0.95F, 0.95F);
|
||||||
|
redStarBuffer.bind();
|
||||||
|
redStarBuffer.drawWithShader(modelViewMatrix, projectionMatrix, GameRenderer.getPositionColorShader());
|
||||||
|
VertexBuffer.unbind();
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VertexBuffer buildRedStarBuffer() {
|
||||||
|
Random random = new Random(552031L);
|
||||||
|
BufferBuilder builder = Tesselator.getInstance().getBuilder();
|
||||||
|
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||||
|
|
||||||
|
for (int i = 0; i < 1800; i++) {
|
||||||
|
double x = random.nextFloat() * 2.0F - 1.0F;
|
||||||
|
double y = random.nextFloat() * 2.0F - 1.0F;
|
||||||
|
double z = random.nextFloat() * 2.0F - 1.0F;
|
||||||
|
double size = 0.13F + random.nextFloat() * 0.12F;
|
||||||
|
double lenSq = x * x + y * y + z * z;
|
||||||
|
if (lenSq < 1.0D && lenSq > 0.01D) {
|
||||||
|
double len = 1.0D / Math.sqrt(lenSq);
|
||||||
|
x *= len;
|
||||||
|
y *= len;
|
||||||
|
z *= len;
|
||||||
|
double sx = x * 100.0D;
|
||||||
|
double sy = y * 100.0D;
|
||||||
|
double sz = z * 100.0D;
|
||||||
|
double yaw = Math.atan2(x, z);
|
||||||
|
double sinYaw = Math.sin(yaw);
|
||||||
|
double cosYaw = Math.cos(yaw);
|
||||||
|
double pitch = Math.atan2(Math.sqrt(x * x + z * z), y);
|
||||||
|
double sinPitch = Math.sin(pitch);
|
||||||
|
double cosPitch = Math.cos(pitch);
|
||||||
|
double rot = random.nextDouble() * Math.PI * 2.0D;
|
||||||
|
double sinRot = Math.sin(rot);
|
||||||
|
double cosRot = Math.cos(rot);
|
||||||
|
float red = 0.75F + random.nextFloat() * 0.25F;
|
||||||
|
float green = 0.02F + random.nextFloat() * 0.05F;
|
||||||
|
float blue = 0.04F + random.nextFloat() * 0.08F;
|
||||||
|
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
double u = (j & 2) - 1;
|
||||||
|
double v = (j + 1 & 2) - 1;
|
||||||
|
double px = u * cosRot - v * sinRot;
|
||||||
|
double py = v * cosRot + u * sinRot;
|
||||||
|
double qx = px * sinPitch;
|
||||||
|
double qy = -px * cosPitch;
|
||||||
|
double rx = qy * sinYaw - py * cosYaw;
|
||||||
|
double ry = py * sinYaw + qy * cosYaw;
|
||||||
|
builder.vertex((float) (sx + rx * size), (float) (sy + qx * size), (float) (sz + ry * size))
|
||||||
|
.color(red, green, blue, 1.0F).endVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferBuilder.RenderedBuffer mesh = builder.end();
|
||||||
|
VertexBuffer buffer = new VertexBuffer(VertexBuffer.Usage.STATIC);
|
||||||
|
buffer.bind();
|
||||||
|
buffer.upload(mesh);
|
||||||
|
VertexBuffer.unbind();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float timeOfDay) {
|
||||||
|
float t = timeOfDay * 360.0F;
|
||||||
|
renderBodyGlow(modelViewMatrix, projectionMatrix, t, -10.0F, 0xFF1F18, 86.0F);
|
||||||
|
renderTexturedBody(modelViewMatrix, projectionMatrix, t, -10.0F, 42.0F, 0xFF1F18, SUN_TEXTURE, false, 0, t * 0.6F);
|
||||||
|
|
||||||
|
float moonOrbit = t + 180.0F;
|
||||||
|
renderBodyGlow(modelViewMatrix, projectionMatrix, moonOrbit, 24.0F, 0x8F30FF, 76.0F);
|
||||||
|
renderTexturedBody(modelViewMatrix, projectionMatrix, moonOrbit, 24.0F, 26.0F, 0x8F30FF, MOON_TEXTURE, true, 0, -t * 0.35F);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderBodyGlow(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float orbitDegrees, float yawDegrees, int rgb, float glowRadius) {
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.disableCull();
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionColorShader);
|
||||||
|
RenderSystem.backupProjectionMatrix();
|
||||||
|
RenderSystem.setProjectionMatrix(projectionMatrix, VertexSorting.DISTANCE_TO_ORIGIN);
|
||||||
|
|
||||||
|
PoseStack modelViewStack = RenderSystem.getModelViewStack();
|
||||||
|
modelViewStack.pushPose();
|
||||||
|
modelViewStack.mulPoseMatrix(modelViewMatrix);
|
||||||
|
modelViewStack.mulPose(Axis.XP.rotationDegrees(orbitDegrees));
|
||||||
|
modelViewStack.mulPose(Axis.ZP.rotationDegrees(yawDegrees));
|
||||||
|
RenderSystem.applyModelViewMatrix();
|
||||||
|
|
||||||
|
float red = ((rgb >> 16) & 0xFF) / 255.0F;
|
||||||
|
float green = ((rgb >> 8) & 0xFF) / 255.0F;
|
||||||
|
float blue = (rgb & 0xFF) / 255.0F;
|
||||||
|
BufferBuilder buffer = Tesselator.getInstance().getBuilder();
|
||||||
|
buffer.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR);
|
||||||
|
buffer.vertex(0.0F, SKY_RADIUS, 0.0F).color(red, green, blue, 0.55F).endVertex();
|
||||||
|
for (int i = 0; i <= 24; i++) {
|
||||||
|
float angle = i * Mth.TWO_PI / 24.0F;
|
||||||
|
buffer.vertex(Mth.sin(angle) * glowRadius, SKY_RADIUS, Mth.cos(angle) * glowRadius).color(red, green, blue, 0.0F).endVertex();
|
||||||
|
}
|
||||||
|
BufferUploader.drawWithShader(buffer.end());
|
||||||
|
|
||||||
|
modelViewStack.popPose();
|
||||||
|
RenderSystem.applyModelViewMatrix();
|
||||||
|
RenderSystem.restoreProjectionMatrix();
|
||||||
|
RenderSystem.enableCull();
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderTexturedBody(
|
||||||
|
Matrix4f modelViewMatrix,
|
||||||
|
Matrix4f projectionMatrix,
|
||||||
|
float orbitDegrees,
|
||||||
|
float yawDegrees,
|
||||||
|
float radius,
|
||||||
|
int rgb,
|
||||||
|
ResourceLocation texture,
|
||||||
|
boolean usePhaseSheet,
|
||||||
|
int phaseIndex,
|
||||||
|
float selfRotationDegrees
|
||||||
|
) {
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.blendFuncSeparate(
|
||||||
|
GlStateManager.SourceFactor.ONE,
|
||||||
|
GlStateManager.DestFactor.ONE,
|
||||||
|
GlStateManager.SourceFactor.ONE,
|
||||||
|
GlStateManager.DestFactor.ZERO
|
||||||
|
);
|
||||||
|
RenderSystem.disableCull();
|
||||||
|
RenderSystem.setShaderColor(((rgb >> 16) & 0xFF) / 255.0F, ((rgb >> 8) & 0xFF) / 255.0F, (rgb & 0xFF) / 255.0F, 1.0F);
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
|
RenderSystem.setShaderTexture(0, texture);
|
||||||
|
RenderSystem.backupProjectionMatrix();
|
||||||
|
RenderSystem.setProjectionMatrix(projectionMatrix, VertexSorting.DISTANCE_TO_ORIGIN);
|
||||||
|
|
||||||
|
PoseStack modelViewStack = RenderSystem.getModelViewStack();
|
||||||
|
modelViewStack.pushPose();
|
||||||
|
modelViewStack.mulPoseMatrix(modelViewMatrix);
|
||||||
|
modelViewStack.mulPose(Axis.XP.rotationDegrees(orbitDegrees));
|
||||||
|
modelViewStack.mulPose(Axis.ZP.rotationDegrees(yawDegrees));
|
||||||
|
modelViewStack.mulPose(Axis.YP.rotationDegrees(selfRotationDegrees));
|
||||||
|
RenderSystem.applyModelViewMatrix();
|
||||||
|
|
||||||
|
float halfSize = BODY_SIZE * 0.5F * (radius / 22.0F);
|
||||||
|
float minU = 0.0F;
|
||||||
|
float maxU = 1.0F;
|
||||||
|
float minV = 0.0F;
|
||||||
|
float maxV = 1.0F;
|
||||||
|
if (usePhaseSheet) {
|
||||||
|
int phaseX = phaseIndex % 4;
|
||||||
|
int phaseY = phaseIndex / 4;
|
||||||
|
minU = phaseX / 4.0F;
|
||||||
|
maxU = (phaseX + 1) / 4.0F;
|
||||||
|
minV = phaseY / 2.0F;
|
||||||
|
maxV = (phaseY + 1) / 2.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferBuilder buffer = Tesselator.getInstance().getBuilder();
|
||||||
|
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
|
||||||
|
buffer.vertex(-halfSize, SKY_RADIUS, -halfSize).uv(minU, minV).endVertex();
|
||||||
|
buffer.vertex(-halfSize, SKY_RADIUS, halfSize).uv(minU, maxV).endVertex();
|
||||||
|
buffer.vertex(halfSize, SKY_RADIUS, halfSize).uv(maxU, maxV).endVertex();
|
||||||
|
buffer.vertex(halfSize, SKY_RADIUS, -halfSize).uv(maxU, minV).endVertex();
|
||||||
|
BufferUploader.drawWithShader(buffer.end());
|
||||||
|
|
||||||
|
modelViewStack.popPose();
|
||||||
|
RenderSystem.applyModelViewMatrix();
|
||||||
|
RenderSystem.restoreProjectionMatrix();
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableCull();
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public class TimeDilationFieldRenderer extends EntityRenderer<TimeDilationFieldEntity> {
|
||||||
|
public TimeDilationFieldRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context);
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(TimeDilationFieldEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
|
||||||
|
MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureLocation(TimeDilationFieldEntity entity) {
|
||||||
|
return new ResourceLocation("minecraft", "textures/misc/unknown_pack.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.VortexModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.vortex.VortexEntity;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
|
||||||
|
public class VortexRenderer extends GeoEntityRenderer<VortexEntity> {
|
||||||
|
public VortexRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new VortexModel());
|
||||||
|
this.shadowRadius = 0.5F;
|
||||||
|
this.withScale(0.9F);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getDeathMaxRotation(VortexEntity animatable) {
|
||||||
|
return 0.0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.entity.vortex.WindVortexEntity;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
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.EntityRendererProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
public class WindVortexRenderer extends EntityRenderer<WindVortexEntity> {
|
||||||
|
private static final int RINGS = 11;
|
||||||
|
private static final float BASE_RADIUS = 0.35F;
|
||||||
|
|
||||||
|
public WindVortexRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context);
|
||||||
|
this.shadowRadius = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(WindVortexEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
|
||||||
|
MultiBufferSource bufferSource, int packedLight) {
|
||||||
|
VertexConsumer consumer = bufferSource.getBuffer(RenderType.lightning());
|
||||||
|
Matrix4f pose = poseStack.last().pose();
|
||||||
|
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;
|
||||||
|
float spinDirection = pull ? -1.0F : 1.0F;
|
||||||
|
|
||||||
|
for (int ring = 0; ring < RINGS; ring++) {
|
||||||
|
float progress = (ring + 0.25F) / RINGS;
|
||||||
|
float axisDistance = progress * height;
|
||||||
|
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 = alphaMultiplier;
|
||||||
|
float halfSide = radius * 0.92F;
|
||||||
|
float lineThickness = Math.max(0.045F, radius * 0.12F);
|
||||||
|
int red = push ? 70 : 64;
|
||||||
|
int green = push ? 220 : 255;
|
||||||
|
int blue = push ? 255 : 56;
|
||||||
|
drawSquareRing(consumer, pose, basis, halfSide, axisDistance, lineThickness, ringSpin,
|
||||||
|
alpha, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawSquareRing(VertexConsumer consumer, Matrix4f pose, WindVortexEntity.Basis basis,
|
||||||
|
float halfSide, float axisDistance, float lineThickness, float rotation, float alpha,
|
||||||
|
int red, int green, int blue) {
|
||||||
|
for (int side = 0; side < 4; side++) {
|
||||||
|
float sideAngle = rotation + side * (Mth.TWO_PI / 4.0F);
|
||||||
|
addSquareSide(consumer, pose, basis, halfSide, axisDistance, lineThickness,
|
||||||
|
sideAngle, alpha, red, green, blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSquareSide(VertexConsumer consumer, Matrix4f pose, WindVortexEntity.Basis basis,
|
||||||
|
float halfSide, float axisDistance, float lineThickness, float sideAngle,
|
||||||
|
float alpha, int red, int green, int blue) {
|
||||||
|
org.joml.Vector3f start = squarePoint(basis, halfSide, axisDistance, sideAngle, -halfSide);
|
||||||
|
org.joml.Vector3f end = squarePoint(basis, halfSide, axisDistance, sideAngle, halfSide);
|
||||||
|
org.joml.Vector3f axisOffset = new org.joml.Vector3f(
|
||||||
|
(float) basis.axis.x, (float) basis.axis.y, (float) basis.axis.z).mul(lineThickness);
|
||||||
|
int a = Mth.clamp((int) (alpha * 255.0F), 0, 255);
|
||||||
|
addQuad(consumer, pose, start, end, axisOffset, red, green, blue, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static org.joml.Vector3f squarePoint(WindVortexEntity.Basis basis, float halfSide,
|
||||||
|
float axisDistance, float sideAngle, float along) {
|
||||||
|
float x = Mth.cos(sideAngle) * halfSide;
|
||||||
|
float z = Mth.sin(sideAngle) * halfSide;
|
||||||
|
float tangentX = -Mth.sin(sideAngle) * along;
|
||||||
|
float tangentZ = Mth.cos(sideAngle) * along;
|
||||||
|
return new org.joml.Vector3f(
|
||||||
|
(float) (basis.axis.x * axisDistance + basis.sideA.x * (x + tangentX) + basis.sideB.x * (z + tangentZ)),
|
||||||
|
(float) (basis.axis.y * axisDistance + basis.sideA.y * (x + tangentX) + basis.sideB.y * (z + tangentZ)),
|
||||||
|
(float) (basis.axis.z * axisDistance + basis.sideA.z * (x + tangentX) + basis.sideB.z * (z + tangentZ)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addQuad(VertexConsumer consumer, Matrix4f pose, org.joml.Vector3f start,
|
||||||
|
org.joml.Vector3f end, org.joml.Vector3f offset, int red, int green, int blue, int alpha) {
|
||||||
|
addVertex(consumer, pose, start.x - offset.x, start.y - offset.y, start.z - offset.z, red, green, blue, alpha);
|
||||||
|
addVertex(consumer, pose, end.x - offset.x, end.y - offset.y, end.z - offset.z, red, green, blue, alpha);
|
||||||
|
addVertex(consumer, pose, end.x + offset.x, end.y + offset.y, end.z + offset.z, red, green, blue, alpha);
|
||||||
|
addVertex(consumer, pose, start.x + offset.x, start.y + offset.y, start.z + offset.z, red, green, blue, alpha);
|
||||||
|
|
||||||
|
// The lightning render type can cull one winding from some camera
|
||||||
|
// angles. Emit the opposite winding as well so every ribbon is
|
||||||
|
// visible from both sides.
|
||||||
|
addVertex(consumer, pose, start.x + offset.x, start.y + offset.y, start.z + offset.z, red, green, blue, alpha);
|
||||||
|
addVertex(consumer, pose, end.x + offset.x, end.y + offset.y, end.z + offset.z, red, green, blue, alpha);
|
||||||
|
addVertex(consumer, pose, end.x - offset.x, end.y - offset.y, end.z - offset.z, red, green, blue, alpha);
|
||||||
|
addVertex(consumer, pose, start.x - offset.x, start.y - offset.y, start.z - offset.z, red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addVertex(VertexConsumer consumer, Matrix4f pose, float x, float y, float z,
|
||||||
|
int red, int green, int blue, int alpha) {
|
||||||
|
consumer.vertex(pose, x, y, z).color(red, green, blue, alpha).endVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTextureLocation(WindVortexEntity entity) {
|
||||||
|
return new ResourceLocation("minecraft", "textures/misc/unknown_pack.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.renderer;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.client.model.WormModel;
|
||||||
|
import com.craisinlord.antarchy.content.entity.WormEntity;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||||
|
|
||||||
|
public class WormRenderer extends GeoEntityRenderer<WormEntity> {
|
||||||
|
public WormRenderer(EntityRendererProvider.Context context) {
|
||||||
|
super(context, new WormModel());
|
||||||
|
this.shadowRadius = 0.75F;
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package com.craisinlord.antarchy.content.client.screen;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.menu.RoyalJudgmentMenu;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
|
||||||
|
public final class RoyalJudgmentScreen extends AbstractContainerScreen<RoyalJudgmentMenu> {
|
||||||
|
private static final ResourceLocation INVENTORY_TEXTURE =
|
||||||
|
new ResourceLocation("minecraft", "textures/gui/container/inventory.png");
|
||||||
|
|
||||||
|
public RoyalJudgmentScreen(RoyalJudgmentMenu menu, Inventory inventory, Component title) {
|
||||||
|
super(menu, inventory, title);
|
||||||
|
this.imageWidth = 176;
|
||||||
|
this.imageHeight = 166;
|
||||||
|
this.inventoryLabelY = 84;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderBg(GuiGraphics graphics, float partialTick, int mouseX, int mouseY) {
|
||||||
|
graphics.blit(INVENTORY_TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight);
|
||||||
|
int gold = 0xD8E6B84A;
|
||||||
|
graphics.fill(this.leftPos - 2, this.topPos - 2, this.leftPos + this.imageWidth + 2, this.topPos, gold);
|
||||||
|
graphics.fill(this.leftPos - 2, this.topPos + this.imageHeight, this.leftPos + this.imageWidth + 2,
|
||||||
|
this.topPos + this.imageHeight + 2, gold);
|
||||||
|
graphics.fill(this.leftPos - 2, this.topPos - 2, this.leftPos, this.topPos + this.imageHeight + 2, gold);
|
||||||
|
graphics.fill(this.leftPos + this.imageWidth, this.topPos - 2, this.leftPos + this.imageWidth + 2,
|
||||||
|
this.topPos + this.imageHeight + 2, gold);
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
package com.craisinlord.antarchy.content.command;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.Commands;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public final class DimensionalTearCommand {
|
||||||
|
private static final double DEFAULT_GAP = 24.0D;
|
||||||
|
private static final double VERTICAL_OFFSET = 1.0D;
|
||||||
|
|
||||||
|
private DimensionalTearCommand() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||||
|
dispatcher.register(
|
||||||
|
Commands.literal("dimensionaltear")
|
||||||
|
.requires(source -> source.hasPermission(2))
|
||||||
|
.executes(context -> spawnPair(context, DEFAULT_GAP))
|
||||||
|
.then(Commands.literal("single")
|
||||||
|
.executes(DimensionalTearCommand::spawnSingle))
|
||||||
|
.then(Commands.argument("blocksAhead", DoubleArgumentType.doubleArg(2.0D, 512.0D))
|
||||||
|
.executes(context -> spawnPair(context, DoubleArgumentType.getDouble(context, "blocksAhead"))))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int lifetime() {
|
||||||
|
return Math.max(1200, AntarchySettings.dimensionalTearLifetimeTicks());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vec3 anchorPos(ServerPlayer player) {
|
||||||
|
Vec3 forward = player.getLookAngle().normalize();
|
||||||
|
return player.position().add(forward.scale(4.0D)).add(0.0D, VERTICAL_OFFSET, 0.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int spawnSingle(CommandContext<CommandSourceStack> context) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
|
||||||
|
ServerPlayer player = context.getSource().getPlayerOrException();
|
||||||
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
DimensionalTearEntity tear = DimensionalTearEntity.create(level, anchorPos(player), player.getYRot(), lifetime());
|
||||||
|
level.addFreshEntity(tear);
|
||||||
|
context.getSource().sendSuccess(() -> Component.literal("Spawned an unlinked dimensional tear"), true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int spawnPair(CommandContext<CommandSourceStack> context, double gap) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
|
||||||
|
ServerPlayer player = context.getSource().getPlayerOrException();
|
||||||
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
|
||||||
|
float yawA = player.getYRot();
|
||||||
|
Vec3 posA = anchorPos(player);
|
||||||
|
Vec3 forward = Vec3.directionFromRotation(0.0F, yawA);
|
||||||
|
Vec3 posB = posA.add(forward.scale(gap));
|
||||||
|
float yawB = Mth.wrapDegrees(yawA + 180.0F);
|
||||||
|
|
||||||
|
DimensionalTearEntity tearA = DimensionalTearEntity.create(level, posA, yawA, lifetime());
|
||||||
|
DimensionalTearEntity tearB = DimensionalTearEntity.create(level, posB, yawB, lifetime());
|
||||||
|
tearA.linkTo(tearB);
|
||||||
|
tearB.linkTo(tearA);
|
||||||
|
level.addFreshEntity(tearA);
|
||||||
|
level.addFreshEntity(tearB);
|
||||||
|
|
||||||
|
int spacing = Mth.floor(gap);
|
||||||
|
context.getSource().sendSuccess(
|
||||||
|
() -> Component.literal("Spawned a linked dimensional tear pair, " + spacing + " blocks apart"),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
package com.craisinlord.antarchy.content.command;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalPunishmentType;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.decree.RoyalDecree;
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.Commands;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
|
||||||
|
public final class RoyalCommand {
|
||||||
|
private RoyalCommand() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||||
|
dispatcher.register(Commands.literal("royal")
|
||||||
|
.requires(source -> source.hasPermission(2))
|
||||||
|
.then(Commands.literal("decree")
|
||||||
|
.then(Commands.argument("id", StringArgumentType.word())
|
||||||
|
.executes(RoyalCommand::activateDecree)))
|
||||||
|
.then(Commands.literal("punishment")
|
||||||
|
.then(Commands.argument("id", StringArgumentType.word())
|
||||||
|
.executes(RoyalCommand::triggerPunishment))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int activateDecree(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
ServerPlayer player = context.getSource().getPlayerOrException();
|
||||||
|
KingEntity king = nearestKing(player);
|
||||||
|
if (king == null) {
|
||||||
|
context.getSource().sendFailure(Component.literal("No King found nearby."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
String id = StringArgumentType.getString(context, "id");
|
||||||
|
if (!king.debugActivateDecree(id, player)) {
|
||||||
|
context.getSource().sendFailure(Component.literal("Unknown decree: " + id));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
context.getSource().sendSuccess(() -> Component.literal("Activated decree " + id), true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int triggerPunishment(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
ServerPlayer player = context.getSource().getPlayerOrException();
|
||||||
|
KingEntity king = nearestKing(player);
|
||||||
|
if (king == null) {
|
||||||
|
context.getSource().sendFailure(Component.literal("No King found nearby."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
String id = StringArgumentType.getString(context, "id");
|
||||||
|
RoyalPunishmentType type = RoyalPunishmentType.fromId(id);
|
||||||
|
if (type == null || !king.debugTriggerPunishment(type, player)) {
|
||||||
|
context.getSource().sendFailure(Component.literal("Unknown or unavailable punishment: " + id));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
context.getSource().sendSuccess(() -> Component.literal("Triggered punishment " + type.id()), true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KingEntity nearestKing(ServerPlayer player) {
|
||||||
|
AABB area = player.getBoundingBox().inflate(128.0D);
|
||||||
|
return player.serverLevel().getEntitiesOfClass(KingEntity.class, area, LivingEntity::isAlive).stream()
|
||||||
|
.min((left, right) -> Double.compare(left.distanceToSqr(player), right.distanceToSqr(player)))
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.craisinlord.antarchy.content.dispenser;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.item.EyeOfTheStormItem;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.BlockSource;
|
||||||
|
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.DispenserBlock;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public class EyeOfTheStormDispenseBehavior extends DefaultDispenseItemBehavior {
|
||||||
|
@Override
|
||||||
|
protected ItemStack execute(BlockSource source, ItemStack stack) {
|
||||||
|
ServerLevel level = source.getLevel();
|
||||||
|
Direction direction = source.getBlockState().getValue(DispenserBlock.FACING);
|
||||||
|
BlockPos pos = source.getPos().relative(direction);
|
||||||
|
Vec3 origin = new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D);
|
||||||
|
Vec3 aim = new Vec3(direction.getStepX(), direction.getStepY(), direction.getStepZ());
|
||||||
|
|
||||||
|
EyeOfTheStormItem.launchStormVortex(level, origin, aim, null);
|
||||||
|
|
||||||
|
if (stack.isDamageableItem()) {
|
||||||
|
stack.setDamageValue(stack.getDamageValue() + 1);
|
||||||
|
if (stack.getDamageValue() >= stack.getMaxDamage()) {
|
||||||
|
stack.shrink(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void playSound(BlockSource source) {
|
||||||
|
source.getLevel().levelEvent(1002, source.getPos(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
package com.craisinlord.antarchy.content.dispenser;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.entity.vortex.VortexChargeProjectileEntity;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.BlockSource;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.DispenserBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.DispenserBlockEntity;
|
||||||
|
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
|
||||||
|
|
||||||
|
public class VortexChargeDispenseBehavior extends DefaultDispenseItemBehavior {
|
||||||
|
@Override
|
||||||
|
protected ItemStack execute(BlockSource source, ItemStack stack) {
|
||||||
|
if (VortexChargeProjectileEntity.defaultItemSupplier == null) {
|
||||||
|
return super.execute(source, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerLevel level = source.getLevel();
|
||||||
|
Direction direction = source.getBlockState().getValue(DispenserBlock.FACING);
|
||||||
|
BlockPos pos = source.getPos().relative(direction);
|
||||||
|
double x = pos.getX() + 0.5D;
|
||||||
|
double y = pos.getY() + 0.5D;
|
||||||
|
double z = pos.getZ() + 0.5D;
|
||||||
|
|
||||||
|
VortexChargeProjectileEntity projectile = new VortexChargeProjectileEntity(level, x, y, z);
|
||||||
|
projectile.shoot(direction.getStepX(), direction.getStepY() + 0.1D, direction.getStepZ(), 1.5F, 1.0F);
|
||||||
|
level.addFreshEntity(projectile);
|
||||||
|
stack.shrink(1);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void playSound(BlockSource source) {
|
||||||
|
source.getLevel().levelEvent(1002, source.getPos(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
|
||||||
|
public final class CommandedBehavior {
|
||||||
|
private CommandedBehavior() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick(Mob mob) {
|
||||||
|
if (!(mob instanceof CommandedEntityAccess access)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UUID commanderUuid = access.antarchy$getCommanderUuid();
|
||||||
|
if (commanderUuid == null || mob.level().getServer() == null) {
|
||||||
|
clear(mob);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entity commander = mob.level() instanceof ServerLevel level ? level.getEntity(commanderUuid) : null;
|
||||||
|
if (commander == null || commander.level() != mob.level() || !RoyalEffectEligibility.canApplyCommanded(mob)) {
|
||||||
|
mob.removeEffect(RoyalEffectHooks.commandedHolder());
|
||||||
|
clear(mob);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LivingEntity target = mob.getTarget();
|
||||||
|
if (target != null && isProtectedAlly(mob, target)) {
|
||||||
|
mob.setTarget(null);
|
||||||
|
target = null;
|
||||||
|
}
|
||||||
|
if (target != null) {
|
||||||
|
tryPassiveCommandedAttack(mob, target);
|
||||||
|
}
|
||||||
|
if (mob.tickCount % 10 != 0 || target != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LivingEntity candidate = commander instanceof Mob commanderMob ? commanderMob.getTarget() : null;
|
||||||
|
if (!isValidTarget(mob, commander, candidate)) {
|
||||||
|
candidate = commander instanceof net.minecraft.world.entity.player.Player player ? player.getLastHurtByMob() : null;
|
||||||
|
}
|
||||||
|
if (!isValidTarget(mob, commander, candidate)) {
|
||||||
|
candidate = mob.getLastHurtByMob();
|
||||||
|
}
|
||||||
|
if (!isValidTarget(mob, commander, candidate)) {
|
||||||
|
candidate = commander instanceof net.minecraft.world.entity.player.Player player ? player.getLastHurtMob() : null;
|
||||||
|
}
|
||||||
|
if (!isValidTarget(mob, commander, candidate)) {
|
||||||
|
candidate = mob.getLastHurtByMob();
|
||||||
|
}
|
||||||
|
if (!isValidTarget(mob, commander, candidate)) {
|
||||||
|
candidate = mob.level().getEntitiesOfClass(LivingEntity.class, mob.getBoundingBox().inflate(16.0D),
|
||||||
|
entity -> isValidTarget(mob, commander, entity)).stream()
|
||||||
|
.min((a, b) -> {
|
||||||
|
int distance = Double.compare(mob.distanceToSqr(a), mob.distanceToSqr(b));
|
||||||
|
return distance != 0 ? distance : Integer.compare(a.getId(), b.getId());
|
||||||
|
}).orElse(null);
|
||||||
|
}
|
||||||
|
if (candidate != null) {
|
||||||
|
mob.setTarget(candidate);
|
||||||
|
access.antarchy$setCommandedTargetOwned(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isProtectedAlly(Mob mob, Entity entity) {
|
||||||
|
if (!(mob instanceof CommandedEntityAccess access) || entity == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UUID commanderUuid = access.antarchy$getCommanderUuid();
|
||||||
|
if (commanderUuid == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (entity.getUUID().equals(commanderUuid)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (entity instanceof Mob other && other instanceof CommandedEntityAccess otherAccess) {
|
||||||
|
return commanderUuid.equals(otherAccess.antarchy$getCommanderUuid());
|
||||||
|
}
|
||||||
|
if (entity instanceof LivingEntity living && living.isAlliedTo(mob)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (entity instanceof LivingEntity living && commanderEntity(mob) instanceof LivingEntity livingCommander) {
|
||||||
|
return living.isAlliedTo(livingCommander);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidTarget(Mob mob, Entity commander, LivingEntity target) {
|
||||||
|
return target != null && target.isAlive() && target != mob && target != commander
|
||||||
|
&& !isProtectedAlly(mob, target) && !target.isAlliedTo(commander);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void tryPassiveCommandedAttack(Mob mob, LivingEntity target) {
|
||||||
|
if (mob.getAttribute(Attributes.ATTACK_DAMAGE) != null
|
||||||
|
&& mob.getAttributeValue(Attributes.ATTACK_DAMAGE) > 0.0D) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mob.getNavigation().moveTo(target, 1.0D);
|
||||||
|
if (mob.tickCount % 20 != 0 || mob.distanceToSqr(target) > 4.0D) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (target.hurt(mob.damageSources().mobAttack(mob), 1.0F)) {
|
||||||
|
mob.swing(net.minecraft.world.InteractionHand.MAIN_HAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Entity commanderEntity(Mob mob) {
|
||||||
|
if (!(mob instanceof CommandedEntityAccess access) || mob.level().getServer() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UUID uuid = access.antarchy$getCommanderUuid();
|
||||||
|
return uuid == null || !(mob.level() instanceof ServerLevel level) ? null : level.getEntity(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear(Mob mob) {
|
||||||
|
if (!(mob instanceof CommandedEntityAccess access)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (access.antarchy$isCommandedTargetOwned()) {
|
||||||
|
mob.setTarget(null);
|
||||||
|
}
|
||||||
|
access.antarchy$setCommandedTargetOwned(false);
|
||||||
|
access.antarchy$setCommanderUuid(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface CommandedEntityAccess {
|
||||||
|
UUID antarchy$getCommanderUuid();
|
||||||
|
void antarchy$setCommanderUuid(UUID uuid);
|
||||||
|
boolean antarchy$isCommandedTargetOwned();
|
||||||
|
void antarchy$setCommandedTargetOwned(boolean owned);
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
import net.minecraft.world.effect.MobEffectCategory;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
|
||||||
|
public final class CommandedMobEffect extends MobEffect {
|
||||||
|
public CommandedMobEffect() {
|
||||||
|
super(MobEffectCategory.HARMFUL, 0xD4AF37);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyEffectTick(LivingEntity entity, int amplifier) {
|
||||||
|
if (!entity.level().isClientSide() && entity instanceof Mob mob) {
|
||||||
|
CommandedBehavior.tick(mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDurationEffectTick(int duration, int amplifier) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
import net.minecraft.world.effect.MobEffectCategory;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
|
||||||
|
/** Accelerates the affected entity's local time rate. */
|
||||||
|
public final class ContractedMobEffect extends MobEffect {
|
||||||
|
public static final double RATE = 2.0D;
|
||||||
|
public static final double STRONG_RATE = 3.0D;
|
||||||
|
|
||||||
|
public ContractedMobEffect() {
|
||||||
|
super(MobEffectCategory.BENEFICIAL, 0xD5A63A);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyEffectTick(LivingEntity entity, int amplifier) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDurationEffectTick(int duration, int amplifier) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double rateForAmplifier(int amplifier) {
|
||||||
|
return amplifier > 0 ? STRONG_RATE : RATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
import net.minecraft.world.effect.MobEffectCategory;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
|
||||||
|
public final class DilatedMobEffect extends MobEffect {
|
||||||
|
public static final double RATE = 0.35D;
|
||||||
|
public static final double STRONG_RATE = 0.2D;
|
||||||
|
|
||||||
|
public DilatedMobEffect() {
|
||||||
|
super(MobEffectCategory.HARMFUL, 0x6C4AB6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyEffectTick(LivingEntity entity, int amplifier) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDurationEffectTick(int duration, int amplifier) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double rateForAmplifier(int amplifier) {
|
||||||
|
return amplifier > 0 ? STRONG_RATE : RATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||||
|
import com.craisinlord.antarchy.content.item.RoyalAssailantArmorItem;
|
||||||
|
import com.craisinlord.antarchy.content.item.RoyalGuardianArmorItem;
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
|
||||||
|
|
||||||
|
public final class RoyalEffectEligibility {
|
||||||
|
private RoyalEffectEligibility() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCommanded(MobEffectInstance instance) {
|
||||||
|
return RoyalEffectHooks.commandedHolder() != null && instance.getEffect() == RoyalEffectHooks.commandedHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDilated(MobEffectInstance instance) {
|
||||||
|
return RoyalEffectHooks.dilatedHolder() != null && instance.getEffect() == RoyalEffectHooks.dilatedHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canApplyCommanded(LivingEntity entity) {
|
||||||
|
return entity instanceof Mob mob
|
||||||
|
&& !(entity instanceof Player)
|
||||||
|
&& !entity.getType().is(AntarchyTags.Entities.COMMANDED_BLACKLIST)
|
||||||
|
&& !(mob.isNoAi())
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.HEAD).getItem() instanceof RoyalGuardianArmorItem)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.CHEST).getItem() instanceof RoyalGuardianArmorItem)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.LEGS).getItem() instanceof RoyalGuardianArmorItem)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET).getItem() instanceof RoyalGuardianArmorItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canApplyDilated(LivingEntity entity) {
|
||||||
|
return !entity.getType().is(AntarchyTags.Entities.DILATED_BLACKLIST)
|
||||||
|
&& !entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.HEAD).getItem() instanceof RoyalAssailantArmorItem)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.CHEST).getItem() instanceof RoyalAssailantArmorItem)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.LEGS).getItem() instanceof RoyalAssailantArmorItem)
|
||||||
|
&& !(entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET).getItem() instanceof RoyalAssailantArmorItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canApply(LivingEntity entity, MobEffectInstance instance) {
|
||||||
|
if (isCommanded(instance)) {
|
||||||
|
return canApplyCommanded(entity);
|
||||||
|
}
|
||||||
|
if (isDilated(instance)) {
|
||||||
|
return canApplyDilated(entity);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canUseCommandSource(Entity source) {
|
||||||
|
return source instanceof Player || source instanceof RoyalBossEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
|
||||||
|
public final class RoyalEffectHooks {
|
||||||
|
private static final ResourceKey<MobEffect> COMMANDED_KEY = ResourceKey.create(Registries.MOB_EFFECT,
|
||||||
|
new ResourceLocation(Antarchy.MODID, "commanded"));
|
||||||
|
private static final ResourceKey<MobEffect> DILATED_KEY = ResourceKey.create(Registries.MOB_EFFECT,
|
||||||
|
new ResourceLocation(Antarchy.MODID, "dilated"));
|
||||||
|
private static final ResourceKey<MobEffect> CONTRACTED_KEY = ResourceKey.create(Registries.MOB_EFFECT,
|
||||||
|
new ResourceLocation(Antarchy.MODID, "contracted"));
|
||||||
|
|
||||||
|
private RoyalEffectHooks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MobEffect commandedHolder() {
|
||||||
|
return BuiltInRegistries.MOB_EFFECT.get(COMMANDED_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MobEffect dilatedHolder() {
|
||||||
|
return BuiltInRegistries.MOB_EFFECT.get(DILATED_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MobEffect contractedHolder() {
|
||||||
|
return BuiltInRegistries.MOB_EFFECT.get(CONTRACTED_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.craisinlord.antarchy.content.effect;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version-neutral eligibility rules for the Dilated effect.
|
||||||
|
*
|
||||||
|
* The 1.21 implementation also checks Royal armor. Those items do not exist
|
||||||
|
* in the 1.20 source set yet, so the shared 1.20 rule set intentionally
|
||||||
|
* retains the entity-tag checks here and can be extended when Royal content
|
||||||
|
* is ported.
|
||||||
|
*/
|
||||||
|
public final class TimeDilationEligibility {
|
||||||
|
private TimeDilationEligibility() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canApply(LivingEntity entity) {
|
||||||
|
return !entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE)
|
||||||
|
&& !entity.getType().is(AntarchyTags.Entities.DILATED_BLACKLIST);
|
||||||
|
}
|
||||||
|
}
|
||||||
+297
@@ -0,0 +1,297 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
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.mixins.LivingEntityJumpingAccessor;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import net.minecraft.world.entity.MoverType;
|
||||||
|
import net.minecraft.world.entity.PathfinderMob;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.animatable.GeoEntity;
|
||||||
|
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimatableManager;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationController;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationState;
|
||||||
|
import software.bernie.geckolib.core.object.PlayState;
|
||||||
|
import software.bernie.geckolib.core.animation.RawAnimation;
|
||||||
|
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||||
|
|
||||||
|
public class HoverboardEntity extends PathfinderMob implements GeoEntity {
|
||||||
|
private static final EntityDataAccessor<Integer> COLOR = SynchedEntityData.defineId(HoverboardEntity.class, EntityDataSerializers.INT);
|
||||||
|
private static final EntityDataAccessor<Boolean> TRAIL_ACTIVE = SynchedEntityData.defineId(HoverboardEntity.class, EntityDataSerializers.BOOLEAN);
|
||||||
|
|
||||||
|
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||||
|
private static final RawAnimation MOVE_ANIM = RawAnimation.begin().thenLoop("move");
|
||||||
|
|
||||||
|
private static final double MAX_HOVER_HEIGHT = 5.0D;
|
||||||
|
private static final double ASCEND_SPEED = 0.2D;
|
||||||
|
private static final double SETTLE_DESCEND_SPEED = 0.08D;
|
||||||
|
private static final double FAST_DESCEND_SPEED = 0.9D;
|
||||||
|
private static final double FAST_DESCEND_RAMP = 0.2D;
|
||||||
|
private static final double DAMPING = 0.955D;
|
||||||
|
private static final double RIDER_Y_OFFSET = 0.65D;
|
||||||
|
|
||||||
|
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||||
|
|
||||||
|
public HoverboardEntity(EntityType<? extends HoverboardEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.setNoGravity(true);
|
||||||
|
this.setPersistenceRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeSupplier.Builder createAttributes() {
|
||||||
|
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hurt(DamageSource source, float amount) {
|
||||||
|
if (this.level().isClientSide || !this.isAlive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.isInvulnerableTo(source)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.ejectPassengers();
|
||||||
|
this.playSound(SoundEvents.WOOD_BREAK, 1.0F, 1.0F);
|
||||||
|
this.spawnAtLocation(this.getDropStack());
|
||||||
|
this.discard();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerGoals() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void defineSynchedData() {
|
||||||
|
super.defineSynchedData();
|
||||||
|
this.entityData.define(COLOR, -1);
|
||||||
|
this.entityData.define(TRAIL_ACTIVE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public DyeColor getColor() {
|
||||||
|
int id = this.entityData.get(COLOR);
|
||||||
|
return id < 0 ? null : DyeColor.byId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(@Nullable DyeColor color) {
|
||||||
|
this.entityData.set(COLOR, color == null ? -1 : color.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTrailActive() {
|
||||||
|
return this.entityData.get(TRAIL_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTrailActive(boolean active) {
|
||||||
|
this.entityData.set(TRAIL_ACTIVE, active);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.addAdditionalSaveData(tag);
|
||||||
|
if (this.getColor() != null) {
|
||||||
|
tag.putString("Color", this.getColor().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.readAdditionalSaveData(tag);
|
||||||
|
this.setColor(tag.contains("Color") ? DyeColor.byName(tag.getString("Color"), null) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InteractionResult mobInteract(Player player, InteractionHand hand) {
|
||||||
|
if (!this.isVehicle() && !player.isSecondaryUseActive()) {
|
||||||
|
if (!this.level().isClientSide) {
|
||||||
|
player.startRiding(this);
|
||||||
|
}
|
||||||
|
return InteractionResult.sidedSuccess(this.level().isClientSide);
|
||||||
|
}
|
||||||
|
return super.mobInteract(player, hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public LivingEntity getControllingPassenger() {
|
||||||
|
Entity passenger = this.getFirstPassenger();
|
||||||
|
return passenger instanceof Player player ? player : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAddPassenger(Entity passenger) {
|
||||||
|
return this.getPassengers().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void positionRider(Entity passenger, MoveFunction moveFunction) {
|
||||||
|
if (!this.hasPassenger(passenger)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vec3 riderOffset = AntarchyGravityRotationUtil.vecPlayerToWorld(
|
||||||
|
0.0D,
|
||||||
|
RIDER_Y_OFFSET,
|
||||||
|
0.0D,
|
||||||
|
AntarchyGravityApi.getGravityDirection(this)
|
||||||
|
);
|
||||||
|
moveFunction.accept(passenger, this.getX() + riderOffset.x, this.getY() + riderOffset.y, this.getZ() + riderOffset.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void travel(Vec3 travelVector) {
|
||||||
|
if (this.getControllingPassenger() instanceof Player rider) {
|
||||||
|
this.setYRot(rider.getYRot());
|
||||||
|
this.yRotO = this.getYRot();
|
||||||
|
this.yBodyRot = this.getYRot();
|
||||||
|
this.yHeadRot = this.getYRot();
|
||||||
|
this.setXRot(0.0F);
|
||||||
|
|
||||||
|
Vec3 look = Vec3.directionFromRotation(0.0F, this.getYRot());
|
||||||
|
float forward = rider.zza;
|
||||||
|
this.setTrailActive(forward > 0.0F);
|
||||||
|
boolean holdingJump = ((LivingEntityJumpingAccessor) rider).antarchy$isJumping();
|
||||||
|
double verticalMotion = this.resolveVerticalMotion(holdingJump);
|
||||||
|
double acceleration = AntarchySettings.hoverboardAcceleration();
|
||||||
|
double maxSpeed = AntarchySettings.hoverboardMaxSpeed();
|
||||||
|
|
||||||
|
Vec3 currentHorizontal = new Vec3(this.getDeltaMovement().x, 0.0D, this.getDeltaMovement().z);
|
||||||
|
Vec3 horizontalMotion = currentHorizontal.scale(DAMPING).add(look.scale(forward * acceleration));
|
||||||
|
|
||||||
|
double horizontalSpeed = horizontalMotion.horizontalDistance();
|
||||||
|
if (horizontalSpeed > maxSpeed) {
|
||||||
|
double scale = maxSpeed / horizontalSpeed;
|
||||||
|
horizontalMotion = new Vec3(horizontalMotion.x * scale, 0.0D, horizontalMotion.z * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setDeltaMovement(horizontalMotion.x, verticalMotion, horizontalMotion.z);
|
||||||
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setTrailActive(false);
|
||||||
|
double verticalMotion = this.resolveVerticalMotion(false);
|
||||||
|
Vec3 horizontalMotion = new Vec3(this.getDeltaMovement().x, 0.0D, this.getDeltaMovement().z).scale(DAMPING);
|
||||||
|
this.setDeltaMovement(horizontalMotion.x, verticalMotion, horizontalMotion.z);
|
||||||
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
||||||
|
}
|
||||||
|
|
||||||
|
private double resolveVerticalMotion(boolean holdingJump) {
|
||||||
|
AntarchyGravityDirection gravityDirection = AntarchyGravityApi.getGravityDirection(this);
|
||||||
|
double supportHeight = this.findSupportHeight(gravityDirection);
|
||||||
|
if (!gravityDirection.isInverted()) {
|
||||||
|
double maxY = supportHeight + MAX_HOVER_HEIGHT;
|
||||||
|
if (holdingJump) {
|
||||||
|
return this.getY() >= maxY ? 0.0D : ASCEND_SPEED;
|
||||||
|
}
|
||||||
|
if (this.getY() <= supportHeight) {
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
|
double heightAboveBand = this.getY() - maxY;
|
||||||
|
if (heightAboveBand > 0.0D) {
|
||||||
|
return -Mth.clamp(SETTLE_DESCEND_SPEED + heightAboveBand * FAST_DESCEND_RAMP, SETTLE_DESCEND_SPEED, FAST_DESCEND_SPEED);
|
||||||
|
}
|
||||||
|
return -SETTLE_DESCEND_SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
double minY = supportHeight - MAX_HOVER_HEIGHT;
|
||||||
|
if (holdingJump) {
|
||||||
|
return this.getY() <= minY ? 0.0D : -ASCEND_SPEED;
|
||||||
|
}
|
||||||
|
if (this.getY() >= supportHeight) {
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
|
double depthBelowBand = minY - this.getY();
|
||||||
|
if (depthBelowBand > 0.0D) {
|
||||||
|
return Mth.clamp(SETTLE_DESCEND_SPEED + depthBelowBand * FAST_DESCEND_RAMP, SETTLE_DESCEND_SPEED, FAST_DESCEND_SPEED);
|
||||||
|
}
|
||||||
|
return SETTLE_DESCEND_SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double findSupportHeight(AntarchyGravityDirection gravityDirection) {
|
||||||
|
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ()));
|
||||||
|
if (!gravityDirection.isInverted()) {
|
||||||
|
int minY = this.level().getMinBuildHeight();
|
||||||
|
while (pos.getY() > minY && this.level().getBlockState(pos).isAir()) {
|
||||||
|
pos.move(Direction.DOWN);
|
||||||
|
}
|
||||||
|
return pos.getY() + 1.0D;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxY = this.level().getMaxBuildHeight() - 1;
|
||||||
|
while (pos.getY() < maxY && this.level().getBlockState(pos).isAir()) {
|
||||||
|
pos.move(Direction.UP);
|
||||||
|
}
|
||||||
|
return this.level().getBlockState(pos).isAir() ? this.getY() : pos.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeWhenFarAway(double distanceToClosestPlayer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void die(DamageSource damageSource) {
|
||||||
|
super.die(damageSource);
|
||||||
|
if (!this.level().isClientSide) {
|
||||||
|
this.spawnAtLocation(this.getDropStack());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getDropStack() {
|
||||||
|
String path = this.getColor() == null ? "hoverboard" : "hoverboard_" + this.getColor().getName();
|
||||||
|
Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, path));
|
||||||
|
return new ItemStack(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getPickResult() {
|
||||||
|
return this.getDropStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||||
|
controllers.add(new AnimationController<>(this, "movement", 0, this::movementController));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayState movementController(AnimationState<HoverboardEntity> state) {
|
||||||
|
return state.setAndContinue(this.getDeltaMovement().horizontalDistanceSqr() > 0.0025D ? MOVE_ANIM : IDLE_ANIM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||||
|
return this.geoCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
+508
@@ -0,0 +1,508 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.QueenEntity;
|
||||||
|
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||||
|
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
|
||||||
|
import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.Difficulty;
|
||||||
|
import net.minecraft.world.DifficultyInstance;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import net.minecraft.world.entity.MobSpawnType;
|
||||||
|
import net.minecraft.world.entity.SpawnGroupData;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.ai.control.FlyingMoveControl;
|
||||||
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
||||||
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||||
|
import net.minecraft.world.entity.monster.Monster;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.ServerLevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.animatable.GeoEntity;
|
||||||
|
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimatableManager;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationController;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationState;
|
||||||
|
import software.bernie.geckolib.core.object.PlayState;
|
||||||
|
import software.bernie.geckolib.core.animation.RawAnimation;
|
||||||
|
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;
|
||||||
|
private static final float STING_CHANCE = 0.35F;
|
||||||
|
private static final int SWARM_RETARGET_INTERVAL = 40;
|
||||||
|
|
||||||
|
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||||
|
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||||
|
private static final RawAnimation FLY_ANIM = RawAnimation.begin().thenLoop("fly");
|
||||||
|
private static final RawAnimation BITE_ANIM = RawAnimation.begin().thenPlay("bite");
|
||||||
|
private static final RawAnimation FLY_BITE_ANIM = RawAnimation.begin().thenPlay("fly_bite");
|
||||||
|
private static final RawAnimation STING_ANIM = RawAnimation.begin().thenPlay("sting");
|
||||||
|
private static final RawAnimation FLY_START_ANIM = RawAnimation.begin().thenPlay("fly_start");
|
||||||
|
private static final RawAnimation FLY_LAND_ANIM = RawAnimation.begin().thenPlay("fly_land");
|
||||||
|
|
||||||
|
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private UUID summonerId;
|
||||||
|
@Nullable
|
||||||
|
private UUID playerSummonerId;
|
||||||
|
private int playerSummonLifetime;
|
||||||
|
private int playerSummonAge;
|
||||||
|
private int lostSummonerTicks;
|
||||||
|
private int takeoffCooldown;
|
||||||
|
private int stingCooldown;
|
||||||
|
private boolean wasFlyingLastTick;
|
||||||
|
|
||||||
|
public ManticoreEntity(EntityType<? extends ManticoreEntity> type, Level level) {
|
||||||
|
super(type, level);
|
||||||
|
this.moveControl = new FlyingMoveControl(this, 10, true);
|
||||||
|
this.xpReward = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fireImmune() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeSupplier.Builder createAttributes() {
|
||||||
|
return Mob.createMobAttributes()
|
||||||
|
.add(Attributes.MAX_HEALTH, AntarchySettings.manticoreHealth())
|
||||||
|
.add(Attributes.MOVEMENT_SPEED, 0.3D)
|
||||||
|
.add(Attributes.FLYING_SPEED, 0.6D)
|
||||||
|
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.manticoreAttackDamage())
|
||||||
|
.add(Attributes.FOLLOW_RANGE, 28.0D)
|
||||||
|
.add(Attributes.KNOCKBACK_RESISTANCE, 0.2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canSpawn(EntityType<ManticoreEntity> type, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||||
|
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER
|
||||||
|
|| spawnReason == MobSpawnType.COMMAND || spawnReason == MobSpawnType.MOB_SUMMONED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pos.getY() >= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BlockState below = level.getBlockState(pos.below());
|
||||||
|
BlockState above = level.getBlockState(pos.above());
|
||||||
|
boolean floorSupport = !below.is(Blocks.BEDROCK) && below.isFaceSturdy(level, pos.below(), Direction.UP);
|
||||||
|
boolean ceilingSupport = !above.is(Blocks.BEDROCK) && above.isFaceSturdy(level, pos.above(), Direction.DOWN);
|
||||||
|
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||||
|
&& (floorSupport || ceilingSupport)
|
||||||
|
&& level.isEmptyBlock(pos)
|
||||||
|
&& Monster.checkMonsterSpawnRules(type, level, spawnReason, pos, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PathNavigation createNavigation(Level level) {
|
||||||
|
FlyingPathNavigation navigation = new FlyingPathNavigation(this, level);
|
||||||
|
navigation.setCanOpenDoors(false);
|
||||||
|
navigation.setCanFloat(true);
|
||||||
|
navigation.setCanPassDoors(true);
|
||||||
|
return navigation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerGoals() {
|
||||||
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||||
|
this.goalSelector.addGoal(1, new ManticoreAttackGoal(this));
|
||||||
|
this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 0.9D));
|
||||||
|
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, 10, true, false, this::canTargetEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData spawnData, @org.jetbrains.annotations.Nullable net.minecraft.nbt.CompoundTag dataTag) {
|
||||||
|
ConfiguredMobSpawnUtil.applyConfiguredHealth(this, AntarchySettings.manticoreHealth());
|
||||||
|
return super.finalizeSpawn(level, difficulty, spawnReason, spawnData, dataTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSummonedBy(UUID queenId) {
|
||||||
|
return queenId.equals(this.summonerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isQueenSummoned() {
|
||||||
|
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()) {
|
||||||
|
if (entity instanceof ManticoreEntity manticore && manticore.isAlive() && manticore.isSummonedBy(queenId)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeWhenFarAway(double distanceToClosestPlayer) {
|
||||||
|
return this.summonerId == null && super.removeWhenFarAway(distanceToClosestPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresCustomPersistence() {
|
||||||
|
return this.summonerId != null || this.playerSummonerId != null || super.requiresCustomPersistence();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (this.level().isClientSide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tickQueenSummonInversionRule();
|
||||||
|
|
||||||
|
boolean flying = !this.onGround();
|
||||||
|
if (flying != this.wasFlyingLastTick) {
|
||||||
|
this.triggerAnim("controller", flying ? "fly_start" : "fly_land");
|
||||||
|
this.wasFlyingLastTick = flying;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.takeoffCooldown > 0) {
|
||||||
|
this.takeoffCooldown--;
|
||||||
|
}
|
||||||
|
if (this.stingCooldown > 0) {
|
||||||
|
this.stingCooldown--;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tickQueenSummonCleanup();
|
||||||
|
|
||||||
|
this.tickPlayerSummon();
|
||||||
|
this.tickQueenSummonTarget();
|
||||||
|
|
||||||
|
LivingEntity target = this.getTarget();
|
||||||
|
if (target != null && target.isAlive()) {
|
||||||
|
if (this.onGround() && this.takeoffCooldown <= 0 && this.distanceToSqr(target) > 6.0D) {
|
||||||
|
this.takeoff();
|
||||||
|
}
|
||||||
|
if (!this.isPlayerSummoned() && !this.isQueenSummoned()
|
||||||
|
&& this.tickCount % SWARM_RETARGET_INTERVAL == 0) {
|
||||||
|
this.spreadSwarmTarget(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else if (this.getTarget() != null && !this.canTargetEntity(this.getTarget())) {
|
||||||
|
this.setTarget(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickQueenSummonTarget() {
|
||||||
|
if (this.summonerId == null || !(this.level() instanceof ServerLevel serverLevel)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entity entity = serverLevel.getEntity(this.summonerId);
|
||||||
|
if (!(entity instanceof QueenEntity queen) || !queen.isAlive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LivingEntity target = queen.getTarget();
|
||||||
|
if (target != null && target.isAlive() && this.canTargetEntity(target)) {
|
||||||
|
this.setTarget(target);
|
||||||
|
} else {
|
||||||
|
this.setTarget(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
Entity summoner = serverLevel.getEntity(this.summonerId);
|
||||||
|
if (summoner instanceof QueenEntity queen && queen.isAlive()) {
|
||||||
|
this.lostSummonerTicks = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (++this.lostSummonerTicks > LOST_SUMMONER_GRACE_TICKS) {
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void takeoff() {
|
||||||
|
this.takeoffCooldown = TAKEOFF_COOLDOWN_TICKS;
|
||||||
|
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.55D, 0.0D));
|
||||||
|
this.hasImpulse = true;
|
||||||
|
LivingEntity target = this.getTarget();
|
||||||
|
if (target != null) {
|
||||||
|
Vec3 above = target.position().add(oppositeGravity(this).scale(3.0D));
|
||||||
|
this.getMoveControl().setWantedPosition(above.x, above.y, above.z, 1.2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void spreadSwarmTarget(LivingEntity currentTarget) {
|
||||||
|
List<Player> nearbyPlayers = this.level().getEntitiesOfClass(
|
||||||
|
Player.class,
|
||||||
|
this.getBoundingBox().inflate(this.getAttributeValue(Attributes.FOLLOW_RANGE)),
|
||||||
|
player -> player.isAlive() && this.canAttack(player));
|
||||||
|
if (nearbyPlayers.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ManticoreEntity> swarm = this.level().getEntitiesOfClass(
|
||||||
|
ManticoreEntity.class,
|
||||||
|
this.getBoundingBox().inflate(32.0D),
|
||||||
|
manticore -> manticore != this && manticore.isAlive());
|
||||||
|
|
||||||
|
Player bestPlayer = null;
|
||||||
|
int bestLoad = Integer.MAX_VALUE;
|
||||||
|
for (Player player : nearbyPlayers) {
|
||||||
|
int load = 0;
|
||||||
|
for (ManticoreEntity manticore : swarm) {
|
||||||
|
if (manticore.getTarget() == player) {
|
||||||
|
load++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (load < bestLoad) {
|
||||||
|
bestLoad = load;
|
||||||
|
bestPlayer = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentLoad = 0;
|
||||||
|
for (ManticoreEntity manticore : swarm) {
|
||||||
|
if (manticore.getTarget() == currentTarget) {
|
||||||
|
currentLoad++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestPlayer != null && bestPlayer != currentTarget && currentLoad - bestLoad >= 2) {
|
||||||
|
this.setTarget(bestPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vec3 oppositeGravity(Entity entity) {
|
||||||
|
return AntarchyGravityApi.isGravityInverted(entity) ? new Vec3(0.0D, -1.0D, 0.0D) : new Vec3(0.0D, 1.0D, 0.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickQueenSummonInversionRule() {
|
||||||
|
if (!this.isQueenSummoned() || this.canUseQueenSummonInversion()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.hasEffect(AntarchyObjects.INVERTED_EFFECT.get())) {
|
||||||
|
this.removeEffect(AntarchyObjects.INVERTED_EFFECT.get());
|
||||||
|
}
|
||||||
|
if (AntarchyGravityApi.isGravityInverted(this) && AntarchyGravityApi.isGravityForced(this)) {
|
||||||
|
AntarchyGravityApi.setGravityDirection(this, AntarchyGravityDirection.DOWN, false);
|
||||||
|
}
|
||||||
|
this.setNoGravity(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canUseQueenSummonInversion() {
|
||||||
|
return ThoraxisUndersideManager.isThoraxis(this.level())
|
||||||
|
&& this.getY() < ThoraxisUndersideManager.GRAVITY_FLIP_Y + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void performSting(LivingEntity enemy) {
|
||||||
|
this.swing(InteractionHand.MAIN_HAND);
|
||||||
|
if (enemy.hurt(this.damageSources().mobAttack(this), (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE))) {
|
||||||
|
enemy.addEffect(new MobEffectInstance(MobEffects.POISON, AntarchySettings.manticoreStingPoisonTicks(), 0), this);
|
||||||
|
}
|
||||||
|
this.stingCooldown = STING_COOLDOWN_TICKS;
|
||||||
|
this.triggerAnim("controller", "sting");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean stingReady() {
|
||||||
|
return this.stingCooldown <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doHurtTarget(Entity target) {
|
||||||
|
boolean hurt = super.doHurtTarget(target);
|
||||||
|
if (hurt) {
|
||||||
|
this.triggerAnim("controller", this.onGround() ? "bite" : "fly_bite");
|
||||||
|
}
|
||||||
|
return hurt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeAffected(MobEffectInstance effectInstance) {
|
||||||
|
if (this.isQueenSummoned() && effectInstance.getEffect() == AntarchyObjects.INVERTED_EFFECT.get()) {
|
||||||
|
return this.canUseQueenSummonInversion();
|
||||||
|
}
|
||||||
|
return super.canBeAffected(effectInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInWall() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.addAdditionalSaveData(tag);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||||
|
controllers.add(new AnimationController<>(this, "controller", 3, this::locomotionPredicate)
|
||||||
|
.triggerableAnim("bite", BITE_ANIM)
|
||||||
|
.triggerableAnim("fly_bite", FLY_BITE_ANIM)
|
||||||
|
.triggerableAnim("sting", STING_ANIM)
|
||||||
|
.triggerableAnim("fly_start", FLY_START_ANIM)
|
||||||
|
.triggerableAnim("fly_land", FLY_LAND_ANIM));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayState locomotionPredicate(AnimationState<ManticoreEntity> state) {
|
||||||
|
if (!this.onGround()) {
|
||||||
|
return state.setAndContinue(FLY_ANIM);
|
||||||
|
}
|
||||||
|
if (state.isMoving()) {
|
||||||
|
return state.setAndContinue(WALK_ANIM);
|
||||||
|
}
|
||||||
|
return state.setAndContinue(IDLE_ANIM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||||
|
return this.geoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ManticoreAttackGoal extends MeleeAttackGoal {
|
||||||
|
private final ManticoreEntity manticore;
|
||||||
|
|
||||||
|
ManticoreAttackGoal(ManticoreEntity manticore) {
|
||||||
|
super(manticore, 1.1D, true);
|
||||||
|
this.manticore = manticore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void checkAndPerformAttack(LivingEntity enemy, double distance) {
|
||||||
|
if (this.isTimeToAttack()) {
|
||||||
|
this.resetAttackCooldown();
|
||||||
|
if (this.manticore.onGround() && this.manticore.stingReady() && this.manticore.getRandom().nextFloat() < STING_CHANCE) {
|
||||||
|
this.manticore.performSting(enemy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.manticore.swing(InteractionHand.MAIN_HAND);
|
||||||
|
this.manticore.doHurtTarget(enemy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.checkAndPerformAttack(enemy, distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.vehicle.Boat;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class RoyalBoatEntity extends Boat {
|
||||||
|
public RoyalBoatEntity(EntityType<? extends Boat> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.setVariant(Type.OAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getDropItem() {
|
||||||
|
return BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, "royal_boat"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.vehicle.Boat;
|
||||||
|
import net.minecraft.world.entity.vehicle.ChestBoat;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class RoyalChestBoatEntity extends ChestBoat {
|
||||||
|
public RoyalChestBoatEntity(EntityType<? extends Boat> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.setVariant(Type.OAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getDropItem() {
|
||||||
|
return BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, "royal_chest_boat"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.vehicle.Boat;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class TruffaloBoatEntity extends Boat {
|
||||||
|
public TruffaloBoatEntity(EntityType<? extends Boat> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.setVariant(Type.OAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getDropItem() {
|
||||||
|
return BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, "truffalo_boat"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.vehicle.Boat;
|
||||||
|
import net.minecraft.world.entity.vehicle.ChestBoat;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class TruffaloChestBoatEntity extends ChestBoat {
|
||||||
|
public TruffaloChestBoatEntity(EntityType<? extends Boat> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.setVariant(Type.OAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getDropItem() {
|
||||||
|
return BuiltInRegistries.ITEM.get(new ResourceLocation(Antarchy.MODID, "truffalo_chest_boat"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.Antarchy;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||||
|
import java.util.Optional;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.MobSpawnType;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||||
|
import net.minecraft.world.entity.monster.Monster;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.ServerLevelAccessor;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.MultifaceBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import software.bernie.geckolib.animatable.GeoEntity;
|
||||||
|
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimatableManager;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationController;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationState;
|
||||||
|
import software.bernie.geckolib.core.object.PlayState;
|
||||||
|
import software.bernie.geckolib.core.animation.RawAnimation;
|
||||||
|
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||||
|
|
||||||
|
public class WormEntity extends Monster implements GeoEntity {
|
||||||
|
private static final ResourceKey<Biome> WORMY_CAVES = ResourceKey.create(
|
||||||
|
Registries.BIOME,
|
||||||
|
new ResourceLocation(Antarchy.MODID, "wormy_caves")
|
||||||
|
);
|
||||||
|
private static final ResourceLocation LOAM_ID = new ResourceLocation(Antarchy.MODID, "loam");
|
||||||
|
private static final ResourceLocation MUCUS_ID = new ResourceLocation(Antarchy.MODID, "mucus");
|
||||||
|
private static final int MUCUS_TRAIL_INTERVAL = 6;
|
||||||
|
private static final double MUCUS_TRAIL_MIN_DISTANCE_SQR = 0.36D;
|
||||||
|
private static final EntityDataAccessor<Integer> ANIMATION_STATE = SynchedEntityData.defineId(WormEntity.class, EntityDataSerializers.INT);
|
||||||
|
private static final int ANIM_INGROUND = 0;
|
||||||
|
private static final int ANIM_CRAWL = 1;
|
||||||
|
private static final int ANIM_ATTACK = 2;
|
||||||
|
private static final RawAnimation INGROUND_ANIM = RawAnimation.begin().thenLoop("inground");
|
||||||
|
private static final RawAnimation CRAWL_ANIM = RawAnimation.begin().thenLoop("crawl");
|
||||||
|
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack");
|
||||||
|
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||||
|
private int attackAnimationTicks;
|
||||||
|
private int mucusTrailCooldown;
|
||||||
|
private Vec3 lastMucusTrailPosition;
|
||||||
|
|
||||||
|
public WormEntity(EntityType<? extends WormEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.xpReward = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeSupplier.Builder createAttributes() {
|
||||||
|
return Monster.createMonsterAttributes()
|
||||||
|
.add(Attributes.MAX_HEALTH, 36.0D)
|
||||||
|
.add(Attributes.MOVEMENT_SPEED, 0.22D)
|
||||||
|
.add(Attributes.ATTACK_DAMAGE, 7.0D)
|
||||||
|
.add(Attributes.FOLLOW_RANGE, 28.0D)
|
||||||
|
.add(Attributes.KNOCKBACK_RESISTANCE, 0.45D);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canSpawn(EntityType<WormEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||||
|
if ((spawnReason == MobSpawnType.NATURAL || spawnReason == MobSpawnType.CHUNK_GENERATION)
|
||||||
|
&& !level.getBiome(pos).is(WORMY_CAVES)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Block> loam = BuiltInRegistries.BLOCK.getOptional(LOAM_ID);
|
||||||
|
BlockState floor = level.getBlockState(pos.below());
|
||||||
|
if (!floor.is(Blocks.MUD) && (loam.isEmpty() || !floor.is(loam.get()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return level.getBlockState(pos.above()).getCollisionShape(level, pos.above()).isEmpty()
|
||||||
|
&& level.getBlockState(pos.above(2)).getCollisionShape(level, pos.above(2)).isEmpty()
|
||||||
|
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void defineSynchedData() {
|
||||||
|
super.defineSynchedData();
|
||||||
|
this.entityData.define(ANIMATION_STATE, ANIM_INGROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerGoals() {
|
||||||
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
||||||
|
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.05D, false));
|
||||||
|
this.goalSelector.addGoal(3, new RandomStrollGoal(this, 0.8D));
|
||||||
|
this.goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 8.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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||||
|
controllers.add(new AnimationController<>(this, "main_controller", 0, this::mainAnimController)
|
||||||
|
.triggerableAnim("attack", ATTACK_ANIM));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayState mainAnimController(AnimationState<WormEntity> state) {
|
||||||
|
return switch (this.entityData.get(ANIMATION_STATE)) {
|
||||||
|
case ANIM_CRAWL -> state.setAndContinue(CRAWL_ANIM);
|
||||||
|
case ANIM_ATTACK -> state.setAndContinue(ATTACK_ANIM);
|
||||||
|
default -> state.setAndContinue(INGROUND_ANIM);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||||
|
return this.geoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doHurtTarget(Entity target) {
|
||||||
|
boolean hurt = super.doHurtTarget(target);
|
||||||
|
if (hurt && target instanceof LivingEntity) {
|
||||||
|
this.attackAnimationTicks = 45;
|
||||||
|
this.entityData.set(ANIMATION_STATE, ANIM_ATTACK);
|
||||||
|
this.triggerAnim("main_controller", "attack");
|
||||||
|
this.playSound(AntarchySoundEvents.MOLEWORM_ATTACK.get(), 0.45F, 0.85F + this.random.nextFloat() * 0.2F);
|
||||||
|
}
|
||||||
|
return hurt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (this.level().isClientSide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tickMucusTrail();
|
||||||
|
if (this.attackAnimationTicks > 0) {
|
||||||
|
this.attackAnimationTicks--;
|
||||||
|
}
|
||||||
|
this.updateAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickMucusTrail() {
|
||||||
|
if (this.mucusTrailCooldown > 0) {
|
||||||
|
this.mucusTrailCooldown--;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 velocity = this.getDeltaMovement();
|
||||||
|
if (this.mucusTrailCooldown > 0
|
||||||
|
|| !this.isAlive()
|
||||||
|
|| !this.onGround()
|
||||||
|
|| this.isInWaterOrBubble()
|
||||||
|
|| this.isPassenger()
|
||||||
|
|| velocity.horizontalDistanceSqr() < 0.0025D) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 currentPosition = this.position();
|
||||||
|
if (this.lastMucusTrailPosition != null
|
||||||
|
&& this.lastMucusTrailPosition.distanceToSqr(currentPosition) < MUCUS_TRAIL_MIN_DISTANCE_SQR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastMucusTrailPosition = currentPosition;
|
||||||
|
this.mucusTrailCooldown = MUCUS_TRAIL_INTERVAL;
|
||||||
|
|
||||||
|
Vec3 horizontalDirection = new Vec3(velocity.x, 0.0D, velocity.z).normalize();
|
||||||
|
BlockPos trailPos = BlockPos.containing(
|
||||||
|
this.getX() - horizontalDirection.x * 0.45D,
|
||||||
|
this.getBoundingBox().minY + 0.01D,
|
||||||
|
this.getZ() - horizontalDirection.z * 0.45D
|
||||||
|
);
|
||||||
|
boolean placed = this.placeFloorMucus(trailPos);
|
||||||
|
|
||||||
|
if (placed && this.random.nextFloat() < 0.35F) {
|
||||||
|
Vec3 side = new Vec3(-horizontalDirection.z, 0.0D, horizontalDirection.x);
|
||||||
|
double sideOffset = this.random.nextBoolean() ? 0.55D : -0.55D;
|
||||||
|
this.placeFloorMucus(BlockPos.containing(
|
||||||
|
trailPos.getX() + 0.5D + side.x * sideOffset,
|
||||||
|
trailPos.getY(),
|
||||||
|
trailPos.getZ() + 0.5D + side.z * sideOffset
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean placeFloorMucus(BlockPos pos) {
|
||||||
|
Optional<Block> mucus = BuiltInRegistries.BLOCK.getOptional(MUCUS_ID);
|
||||||
|
if (mucus.isEmpty() || !(mucus.get() instanceof MultifaceBlock mucusBlock)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockState existing = this.level().getBlockState(pos);
|
||||||
|
var floorFace = MultifaceBlock.getFaceProperty(Direction.DOWN);
|
||||||
|
if (existing.is(mucusBlock)) {
|
||||||
|
if (existing.getValue(floorFace)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BlockState merged = existing.setValue(floorFace, true);
|
||||||
|
if (merged.canSurvive(this.level(), pos)) {
|
||||||
|
this.level().setBlock(pos, merged, Block.UPDATE_ALL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existing.isAir()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos supportPos = pos.below();
|
||||||
|
BlockState support = this.level().getBlockState(supportPos);
|
||||||
|
if (!support.isFaceSturdy(this.level(), supportPos, Direction.UP)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockState mucusState = mucusBlock.defaultBlockState().setValue(floorFace, true);
|
||||||
|
if (!mucusState.canSurvive(this.level(), pos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.level().setBlock(pos, mucusState, Block.UPDATE_ALL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAnimationState() {
|
||||||
|
if (this.attackAnimationTicks > 0) {
|
||||||
|
this.entityData.set(ANIMATION_STATE, ANIM_ATTACK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vec3 velocity = this.getDeltaMovement();
|
||||||
|
if (this.getTarget() != null || velocity.horizontalDistanceSqr() > 1.0E-4D || this.walkAnimation.speed() > 0.03F) {
|
||||||
|
this.entityData.set(ANIMATION_STATE, ANIM_CRAWL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.entityData.set(ANIMATION_STATE, ANIM_INGROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Entity.MovementEmission getMovementEmission() {
|
||||||
|
return Entity.MovementEmission.EVENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent getAmbientSound() {
|
||||||
|
return AntarchySoundEvents.MOLEWORM_AMBIENT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent getHurtSound(DamageSource damageSource) {
|
||||||
|
return AntarchySoundEvents.MOLEWORM_HURT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent getDeathSound() {
|
||||||
|
return AntarchySoundEvents.MOLEWORM_HURT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void playStepSound(BlockPos pos, BlockState state) {
|
||||||
|
this.playSound(AntarchySoundEvents.MOLEWORM_DIG.get(), 0.15F, 0.9F + this.random.nextFloat() * 0.2F);
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.item.WormHookTetherManager;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.entity.projectile.ThrowableItemProjectile;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.minecraft.world.phys.EntityHitResult;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown out by the worm hook item. Sticks into the first block it hits and stays there,
|
||||||
|
* anchoring a swing rope back to the thrower until the player detaches.
|
||||||
|
*/
|
||||||
|
public class WormHookProjectileEntity extends ThrowableItemProjectile {
|
||||||
|
private boolean stuck = false;
|
||||||
|
|
||||||
|
public WormHookProjectileEntity(EntityType<? extends WormHookProjectileEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WormHookProjectileEntity(EntityType<? extends WormHookProjectileEntity> entityType, LivingEntity owner, Level level) {
|
||||||
|
super(entityType, owner, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Item getDefaultItem() {
|
||||||
|
return AntarchyObjects.WORM_HOOK.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (this.stuck) {
|
||||||
|
this.setDeltaMovement(net.minecraft.world.phys.Vec3.ZERO);
|
||||||
|
}
|
||||||
|
super.tick();
|
||||||
|
|
||||||
|
if (!this.level().isClientSide && !this.stuck && this.tickCount > 200) {
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHit(HitResult hitResult) {
|
||||||
|
if (this.level().isClientSide || this.stuck) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitResult instanceof EntityHitResult) {
|
||||||
|
this.discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hitResult instanceof BlockHitResult blockHit)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(this.getOwner() instanceof ServerPlayer player)) {
|
||||||
|
this.discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stuck = true;
|
||||||
|
this.setDeltaMovement(net.minecraft.world.phys.Vec3.ZERO);
|
||||||
|
this.setNoGravity(true);
|
||||||
|
this.setPos(blockHit.getLocation());
|
||||||
|
|
||||||
|
boolean attached = WormHookTetherManager.attach(player, this);
|
||||||
|
if (!attached) {
|
||||||
|
this.discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.level().playSound(null, this.blockPosition(), SoundEvents.FISHING_BOBBER_THROW, SoundSource.PLAYERS, 0.8F, 1.4F);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStuck() {
|
||||||
|
return this.stuck;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeHook() {
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1007
File diff suppressed because it is too large.
Load diff
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class PrinceEntity extends RoyalMountEntity {
|
||||||
|
public PrinceEntity(EntityType<? extends PrinceEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeSupplier.Builder createAttributes() {
|
||||||
|
return RoyalMountEntity.createBaseAttributes(
|
||||||
|
AntarchySettings.princeHealth(), AntarchySettings.princeAttackDamage(),
|
||||||
|
AntarchySettings.princeMovementSpeed(), AntarchySettings.princeFlyingSpeed(),
|
||||||
|
AntarchySettings.princeArmor(), AntarchySettings.princeKnockbackResistance(),
|
||||||
|
AntarchySettings.princeFollowRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RoyalBeamElement boltElement(int lateralIndex) {
|
||||||
|
return switch (lateralIndex) {
|
||||||
|
case -1 -> RoyalBeamElement.FIRE;
|
||||||
|
case 1 -> RoyalBeamElement.ICE;
|
||||||
|
default -> RoyalBeamElement.LIGHTNING;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String geoName() {
|
||||||
|
return "prince";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected EntityType<? extends RoyalBoltEntity> boltType() {
|
||||||
|
return AntarchyObjects.ROYAL_BOLT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent idleSound() {
|
||||||
|
return AntarchySoundEvents.PRINCE_IDLE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent biteSound() {
|
||||||
|
return AntarchySoundEvents.PRINCE_BITE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent shootSound() {
|
||||||
|
return AntarchySoundEvents.PRINCE_SHOOT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent flySound() {
|
||||||
|
return AntarchySoundEvents.PRINCE_FLY.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent stepSound() {
|
||||||
|
return AntarchySoundEvents.PRINCE_STEP.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class PrincessEntity extends RoyalMountEntity {
|
||||||
|
public PrincessEntity(EntityType<? extends PrincessEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeSupplier.Builder createAttributes() {
|
||||||
|
return RoyalMountEntity.createBaseAttributes(
|
||||||
|
AntarchySettings.princessHealth(), AntarchySettings.princessAttackDamage(),
|
||||||
|
AntarchySettings.princessMovementSpeed(), AntarchySettings.princessFlyingSpeed(),
|
||||||
|
AntarchySettings.princessArmor(), AntarchySettings.princessKnockbackResistance(),
|
||||||
|
AntarchySettings.princessFollowRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RoyalBeamElement boltElement(int lateralIndex) {
|
||||||
|
return switch (lateralIndex) {
|
||||||
|
case -1 -> RoyalBeamElement.FIRE;
|
||||||
|
case 1 -> RoyalBeamElement.ICE;
|
||||||
|
default -> RoyalBeamElement.LIGHTNING;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String geoName() {
|
||||||
|
return "princess";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected EntityType<? extends RoyalBoltEntity> boltType() {
|
||||||
|
return AntarchyObjects.ROYAL_BOLT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent idleSound() {
|
||||||
|
return AntarchySoundEvents.PRINCESS_IDLE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent biteSound() {
|
||||||
|
return AntarchySoundEvents.PRINCESS_BITE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent shootSound() {
|
||||||
|
return AntarchySoundEvents.PRINCESS_SHOOT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent flySound() {
|
||||||
|
return AntarchySoundEvents.PRINCESS_FLY.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent stepSound() {
|
||||||
|
return AntarchySoundEvents.PRINCESS_STEP.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
+542
@@ -0,0 +1,542 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||||
|
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
|
||||||
|
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.particles.DustParticleOptions;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.BossEvent;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.MobSpawnType;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackLane;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.attack.RoyalEffectController;
|
||||||
|
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
|
||||||
|
import com.craisinlord.antarchy.content.time.TimeDilationApi;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
public class QueenEntity extends RoyalBossEntity {
|
||||||
|
private static final String SUMMON_COOLDOWN_KEY = "ManticoreSummonCooldownTicks";
|
||||||
|
private static final int FAILED_SUMMON_RETRY_TICKS = 20;
|
||||||
|
private static final int POSITION_ATTEMPTS_PER_MANTICORE = 8;
|
||||||
|
|
||||||
|
private static final ResourceLocation ACCEL_SPEED_ID = new ResourceLocation("antarchy", "queen_royal_acceleration_speed");
|
||||||
|
private static final ResourceLocation ACCEL_FLY_ID = new ResourceLocation("antarchy", "queen_royal_acceleration_fly");
|
||||||
|
private static final DustParticleOptions ACCEL_DUST = new DustParticleOptions(new org.joml.Vector3f(1.0F, 0.15F, 0.15F), 2.0F);
|
||||||
|
|
||||||
|
private static final int GRAVITY_STOMP_COOLDOWN = 180;
|
||||||
|
private static final int TIME_FIELD_COOLDOWN = 260;
|
||||||
|
private static final int MOMENTUM_LOCK_COOLDOWN = 420;
|
||||||
|
private static final int MOMENTUM_LOCK_DURATION = 90;
|
||||||
|
private static final double MOMENTUM_LOCK_RADIUS = 26.0D;
|
||||||
|
private static final int CRUSHING_GRAVITY_COOLDOWN = 360;
|
||||||
|
private static final int CRUSHING_GRAVITY_DURATION = 120;
|
||||||
|
private static final double CRUSHING_GRAVITY_RADIUS = 18.0D;
|
||||||
|
private static final double CRUSHING_GRAVITY_STRENGTH = 0.12D;
|
||||||
|
private static final int ACCELERATION_COOLDOWN = 500;
|
||||||
|
private static final int ACCELERATION_DURATION = 140;
|
||||||
|
private static final int BLACK_HOLE_COOLDOWN = 340;
|
||||||
|
|
||||||
|
private int manticoreSummonCooldownTicks;
|
||||||
|
private int gravityStompCooldownTicks;
|
||||||
|
private int timeFieldCooldownTicks;
|
||||||
|
private int momentumLockCooldownTicks;
|
||||||
|
private int crushingGravityCooldownTicks;
|
||||||
|
private int accelerationCooldownTicks;
|
||||||
|
private int blackHoleCooldownTicks;
|
||||||
|
private int idleWanderCooldownTicks;
|
||||||
|
private int finalTimeControlTicks;
|
||||||
|
private final RoyalEffectController royalEffects = new RoyalEffectController();
|
||||||
|
private final Map<UUID, Vec3> frozenVelocities = new HashMap<>();
|
||||||
|
|
||||||
|
public QueenEntity(EntityType<? extends QueenEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeSupplier.Builder createAttributes() {
|
||||||
|
return createBaseAttributes(AntarchySettings.queenHealth(), AntarchySettings.queenAttackDamage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String geoName() {
|
||||||
|
return "queen";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isFlyingBoss() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalFlyLoopSound() {
|
||||||
|
return AntarchySoundEvents.QUEEN_FLY_LOOP.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BossEvent.BossBarColor bossBarColor() {
|
||||||
|
return BossEvent.BossBarColor.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalIdleSound() {
|
||||||
|
return AntarchySoundEvents.QUEEN_IDLE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalHurtSound() {
|
||||||
|
return AntarchySoundEvents.QUEEN_HURT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalDeathSound() {
|
||||||
|
return AntarchySoundEvents.QUEEN_DEATH.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalBiteSound() {
|
||||||
|
return AntarchySoundEvents.QUEEN_BITE.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalBeamShootSound() { return AntarchySoundEvents.QUEEN_BEAM_SHOOT.get(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalBeamStartSound() { return AntarchySoundEvents.QUEEN_BEAM_START.get(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalBeamLoopSound() { return AntarchySoundEvents.QUEEN_BEAM_LOOP.get(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SoundEvent royalBeamEndSound() { return AntarchySoundEvents.QUEEN_BEAM_END.get(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RoyalBeamSettings royalBeamSettings() {
|
||||||
|
return new RoyalBeamSettings(AntarchySettings.queenBeamRange(), AntarchySettings.queenBeamTracking(), 7.5D,
|
||||||
|
AntarchySettings.queenBeamDurationTicks(), AntarchySettings.queenBeamTravelTicks(), AntarchySettings.queenBeamCooldownTicks(), 6.0F, 6.0F,
|
||||||
|
(float) AntarchySettings.queenBeamDamage(), 1.0F, 3, 100.0D,
|
||||||
|
(float) AntarchySettings.queenBeamTerrainRadius(), 4.0F, AntarchySettings.queenBeamTerrainCap(),
|
||||||
|
1.0F, 0.08F, 15.0F, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RoyalBeamTerrainMode royalBeamTerrainMode() {
|
||||||
|
return RoyalBeamTerrainMode.DESTROY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isFieldImmune(Entity entity) {
|
||||||
|
return entity instanceof RoyalBossEntity
|
||||||
|
|| entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (this.level().isClientSide || this.isDeadOrDying()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LivingEntity target = this.getTarget();
|
||||||
|
if (target == null || !target.isAlive() || !this.canAttack(target)) {
|
||||||
|
this.tickIdleWander();
|
||||||
|
}
|
||||||
|
this.tickManticoreSummon();
|
||||||
|
this.tickQueenAbilities();
|
||||||
|
this.tickFinalTimeControl();
|
||||||
|
this.royalEffects.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickIdleWander() {
|
||||||
|
if (this.idleWanderCooldownTicks-- > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.idleWanderCooldownTicks = 80 + this.random.nextInt(80);
|
||||||
|
double angle = this.random.nextDouble() * Mth.TWO_PI;
|
||||||
|
double radius = 6.0D + this.random.nextDouble() * 14.0D;
|
||||||
|
double wx = this.getX() + Math.cos(angle) * radius;
|
||||||
|
double wz = this.getZ() + Math.sin(angle) * radius;
|
||||||
|
this.getMoveControl().setWantedPosition(wx, this.groundYBelow(wx, wz), wz, 0.7D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int cooldown(int base) {
|
||||||
|
return this.royalEffects.active("acceleration") ? Math.max(20, base / 2) : base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickManticoreSummon() {
|
||||||
|
if (this.manticoreSummonCooldownTicks > 0) {
|
||||||
|
this.manticoreSummonCooldownTicks--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LivingEntity target = this.getTarget();
|
||||||
|
if (target == null || !target.isAlive() || !this.canAttack(target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentCount = ManticoreEntity.countSummonedBy(serverLevel, this.getUUID());
|
||||||
|
int cap = AntarchySettings.queenManticoreCap();
|
||||||
|
if (currentCount >= cap) {
|
||||||
|
this.manticoreSummonCooldownTicks = FAILED_SUMMON_RETRY_TICKS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int summonCount = Math.min(AntarchySettings.queenManticoreSummonCount(), cap - currentCount);
|
||||||
|
int spawned = this.spawnManticoreTears(serverLevel, target, summonCount);
|
||||||
|
|
||||||
|
this.manticoreSummonCooldownTicks = spawned > 0
|
||||||
|
? this.cooldown(Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks()))
|
||||||
|
: FAILED_SUMMON_RETRY_TICKS;
|
||||||
|
if (spawned > 0) {
|
||||||
|
this.triggerAnim("body_action", "minion_spawn");
|
||||||
|
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.8F + this.random.nextFloat() * 0.12F);
|
||||||
|
serverLevel.sendParticles(ParticleTypes.PORTAL, this.getX(), this.getY() + 2.0D, this.getZ(),
|
||||||
|
36, 3.0D, 2.0D, 3.0D, 0.15D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int spawnManticoreTears(ServerLevel level, LivingEntity target, int count) {
|
||||||
|
int pairs = Math.max(1, Math.min(3, (count + 1) / 2));
|
||||||
|
int created = 0;
|
||||||
|
for (int i = 0; i < pairs; i++) {
|
||||||
|
double angle = this.random.nextDouble() * Mth.TWO_PI;
|
||||||
|
double distance = Math.max(8.0D, AntarchySettings.queenManticoreSummonRange() * 0.65D);
|
||||||
|
Vec3 firstPos = this.position().add(Math.cos(angle) * distance, 1.0D, Math.sin(angle) * distance);
|
||||||
|
Vec3 secondPos = this.position().add(Math.cos(angle + Math.PI) * distance, 1.0D, Math.sin(angle + Math.PI) * distance);
|
||||||
|
DimensionalTearEntity first = DimensionalTearEntity.createQueenManticoreTear(level, firstPos,
|
||||||
|
(float) Math.toDegrees(angle), 240, this.getUUID(), Math.min(3, count));
|
||||||
|
DimensionalTearEntity second = DimensionalTearEntity.createQueenManticoreTear(level, secondPos,
|
||||||
|
(float) Math.toDegrees(angle + Math.PI), 240, this.getUUID(), Math.min(3, count));
|
||||||
|
first.linkTo(second);
|
||||||
|
second.linkTo(first);
|
||||||
|
level.addFreshEntity(first);
|
||||||
|
level.addFreshEntity(second);
|
||||||
|
created++;
|
||||||
|
}
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickQueenAbilities() {
|
||||||
|
if (this.gravityStompCooldownTicks > 0) this.gravityStompCooldownTicks--;
|
||||||
|
if (this.timeFieldCooldownTicks > 0) this.timeFieldCooldownTicks--;
|
||||||
|
if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--;
|
||||||
|
if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--;
|
||||||
|
if (this.accelerationCooldownTicks > 0) this.accelerationCooldownTicks--;
|
||||||
|
if (this.blackHoleCooldownTicks > 0) this.blackHoleCooldownTicks--;
|
||||||
|
|
||||||
|
if (!(this.level() instanceof ServerLevel level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LivingEntity target = this.getTarget();
|
||||||
|
if (target == null || !target.isAlive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Phase phase = this.phase();
|
||||||
|
boolean majorBusy = this.royalEffects.active("momentum_lock") || this.royalEffects.active("crushing_gravity");
|
||||||
|
boolean allowOverlap = phase != Phase.ONE;
|
||||||
|
String[] candidates = phase == Phase.ONE
|
||||||
|
? new String[] {"gravity_stomp", "time_field", "black_hole"}
|
||||||
|
: new String[] {"gravity_stomp", "time_field", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"};
|
||||||
|
String selected = this.attackScheduler.chooseWeighted(candidates, id -> {
|
||||||
|
if (!this.queenAttackReady(id, phase, majorBusy, allowOverlap)) return 0;
|
||||||
|
return switch (id) {
|
||||||
|
case "black_hole", "crushing_gravity" -> phase == Phase.THREE ? 5 : 3;
|
||||||
|
case "momentum_lock", "acceleration" -> phase == Phase.THREE ? 4 : 2;
|
||||||
|
default -> 4;
|
||||||
|
};
|
||||||
|
}, this.random);
|
||||||
|
if (selected == null) return;
|
||||||
|
int cooldown = switch (selected) {
|
||||||
|
case "gravity_stomp" -> GRAVITY_STOMP_COOLDOWN;
|
||||||
|
case "time_field" -> TIME_FIELD_COOLDOWN;
|
||||||
|
case "black_hole" -> BLACK_HOLE_COOLDOWN;
|
||||||
|
case "momentum_lock" -> MOMENTUM_LOCK_COOLDOWN;
|
||||||
|
case "crushing_gravity" -> CRUSHING_GRAVITY_COOLDOWN;
|
||||||
|
default -> ACCELERATION_COOLDOWN;
|
||||||
|
};
|
||||||
|
if (!this.beginRoyalAttack(selected, RoyalAttackLane.HAZARD, this.cooldown(cooldown),
|
||||||
|
10, 1, 14, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() {
|
||||||
|
@Override public void onStart() {
|
||||||
|
if ("gravity_stomp".equals(selected)) {
|
||||||
|
QueenEntity.this.triggerAnim("body_action", "stomp");
|
||||||
|
} else {
|
||||||
|
QueenEntity.this.triggerAnim("body_action", "wing_gust");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override public void onActive(int elapsedTicks) {
|
||||||
|
QueenEntity.this.performSelectedQueenAttack(selected, phase, cooldown, level, target);
|
||||||
|
}
|
||||||
|
})) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performSelectedQueenAttack(String selected, Phase phase, int cooldown,
|
||||||
|
ServerLevel level, LivingEntity target) {
|
||||||
|
switch (selected) {
|
||||||
|
case "gravity_stomp" -> { this.gravityStompCooldownTicks = cooldown; this.performGravityStomp(level, target); }
|
||||||
|
case "time_field" -> {
|
||||||
|
this.timeFieldCooldownTicks = cooldown;
|
||||||
|
double fieldRate = phase == Phase.THREE ? 0.1D : 0.4D;
|
||||||
|
TimeDilationApi.createField(level, target.position(), 12.0D, fieldRate, 140 + phase.ordinal() * 40);
|
||||||
|
}
|
||||||
|
case "black_hole" -> { this.blackHoleCooldownTicks = cooldown; this.castBlackHole(level, target); }
|
||||||
|
case "momentum_lock" -> { this.momentumLockCooldownTicks = cooldown; this.startMomentumLock(level); }
|
||||||
|
case "crushing_gravity" -> {
|
||||||
|
this.crushingGravityCooldownTicks = cooldown;
|
||||||
|
this.royalEffects.start("crushing_gravity", CRUSHING_GRAVITY_DURATION,
|
||||||
|
() -> {}, this::tickCrushingGravity, () -> {});
|
||||||
|
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.6F);
|
||||||
|
}
|
||||||
|
case "acceleration" -> { this.accelerationCooldownTicks = cooldown; this.startRoyalAcceleration(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean queenAttackReady(String id, Phase phase, boolean majorBusy, boolean allowOverlap) {
|
||||||
|
if (!allowOverlap && majorBusy) return false;
|
||||||
|
return switch (id) {
|
||||||
|
case "gravity_stomp" -> this.gravityStompCooldownTicks <= 0;
|
||||||
|
case "time_field" -> this.timeFieldCooldownTicks <= 0;
|
||||||
|
case "black_hole" -> this.blackHoleCooldownTicks <= 0;
|
||||||
|
case "momentum_lock" -> !this.royalEffects.active("momentum_lock") && this.momentumLockCooldownTicks <= 0;
|
||||||
|
case "crushing_gravity" -> phase != Phase.ONE && !this.royalEffects.active("crushing_gravity") && this.crushingGravityCooldownTicks <= 0;
|
||||||
|
case "acceleration" -> phase == Phase.THREE && !this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0;
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickFinalTimeControl() {
|
||||||
|
if (this.phase() != Phase.THREE || !(this.level() instanceof ServerLevel level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0) {
|
||||||
|
this.startRoyalAcceleration();
|
||||||
|
}
|
||||||
|
if (this.finalTimeControlTicks-- <= 0) {
|
||||||
|
this.finalTimeControlTicks = AntarchySettings.queenFinalTimeFieldCooldownTicks();
|
||||||
|
LivingEntity target = this.getTarget();
|
||||||
|
if (target != null && target.isAlive()) {
|
||||||
|
TimeDilationApi.createField(level, target.position(), AntarchySettings.queenFinalTimeFieldRadius(),
|
||||||
|
AntarchySettings.queenFinalTimeFieldRate(), AntarchySettings.queenFinalTimeFieldDurationTicks());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performGravityStomp(ServerLevel level, LivingEntity target) {
|
||||||
|
this.triggerAnim("body_action", "stomp");
|
||||||
|
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.7F);
|
||||||
|
DamageSource source = this.damageSources().mobAttack(this);
|
||||||
|
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
|
||||||
|
this.getBoundingBox().inflate(18.0D), entity -> entity instanceof Player && entity.isAlive())) {
|
||||||
|
double distance = Math.max(1.0D, living.distanceTo(this));
|
||||||
|
living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D)));
|
||||||
|
Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D);
|
||||||
|
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z));
|
||||||
|
living.hasImpulse = true;
|
||||||
|
}
|
||||||
|
level.sendParticles(ParticleTypes.SONIC_BOOM, this.getX(), this.getY() + 1.0D, this.getZ(), 4, 2.0D, 0.5D, 2.0D, 0.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void castBlackHole(ServerLevel level, LivingEntity target) {
|
||||||
|
Vec3 anchor = target.position().add(
|
||||||
|
(this.random.nextDouble() - 0.5D) * 4.0D,
|
||||||
|
2.0D + this.random.nextDouble() * 2.0D,
|
||||||
|
(this.random.nextDouble() - 0.5D) * 4.0D);
|
||||||
|
RoyalBlackHoleEntity hole = RoyalBlackHoleEntity.create(level, anchor, this.getUUID());
|
||||||
|
level.addFreshEntity(hole);
|
||||||
|
this.triggerAnim("body_action", "wing_gust");
|
||||||
|
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.0F);
|
||||||
|
level.sendParticles(ParticleTypes.REVERSE_PORTAL, anchor.x, anchor.y, anchor.z, 60, 1.0D, 1.0D, 1.0D, 0.4D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startMomentumLock(ServerLevel level) {
|
||||||
|
this.momentumLockCooldownTicks = MOMENTUM_LOCK_COOLDOWN;
|
||||||
|
this.royalEffects.start("momentum_lock", MOMENTUM_LOCK_DURATION, () -> {
|
||||||
|
this.frozenVelocities.clear();
|
||||||
|
for (Entity entity : level.getEntitiesOfClass(Entity.class,
|
||||||
|
this.getBoundingBox().inflate(MOMENTUM_LOCK_RADIUS))) {
|
||||||
|
if (entity == this || isFieldImmune(entity) || entity instanceof Player player && player.isCreative()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.frozenVelocities.put(entity.getUUID(), entity.getDeltaMovement());
|
||||||
|
}
|
||||||
|
}, this::tickMomentumLockEffect, () -> {
|
||||||
|
for (Map.Entry<UUID, Vec3> entry : this.frozenVelocities.entrySet()) {
|
||||||
|
Entity entity = level.getEntity(entry.getKey());
|
||||||
|
if (entity != null && entity.isAlive()) {
|
||||||
|
entity.setDeltaMovement(entry.getValue());
|
||||||
|
entity.hasImpulse = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.frozenVelocities.clear();
|
||||||
|
});
|
||||||
|
this.triggerAnim("body_action", "wing_gust");
|
||||||
|
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.5F);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickMomentumLockEffect() {
|
||||||
|
if (!(this.level() instanceof ServerLevel level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Map.Entry<UUID, Vec3> entry : this.frozenVelocities.entrySet()) {
|
||||||
|
Entity entity = level.getEntity(entry.getKey());
|
||||||
|
if (entity == null || !entity.isAlive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entity.setDeltaMovement(Vec3.ZERO);
|
||||||
|
entity.fallDistance = 0.0F;
|
||||||
|
entity.hasImpulse = true;
|
||||||
|
if (this.tickCount % 6 == 0) {
|
||||||
|
level.sendParticles(ParticleTypes.END_ROD, entity.getX(), entity.getY() + entity.getBbHeight() * 0.5D, entity.getZ(),
|
||||||
|
2, 0.2D, 0.2D, 0.2D, 0.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickCrushingGravity() {
|
||||||
|
if (!this.royalEffects.active("crushing_gravity")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(this.level() instanceof ServerLevel level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Entity entity : level.getEntitiesOfClass(Entity.class,
|
||||||
|
this.getBoundingBox().inflate(CRUSHING_GRAVITY_RADIUS))) {
|
||||||
|
if (entity == this || isFieldImmune(entity) || entity instanceof Player player && player.isCreative()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double downSign = AntarchyGravityApi.isGravityInverted(entity) ? 1.0D : -1.0D;
|
||||||
|
entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, downSign * CRUSHING_GRAVITY_STRENGTH, 0.0D));
|
||||||
|
entity.hasImpulse = true;
|
||||||
|
}
|
||||||
|
if (this.tickCount % 8 == 0) {
|
||||||
|
level.sendParticles(ACCEL_DUST, this.getX(), this.getY() + 1.0D, this.getZ(), 20,
|
||||||
|
CRUSHING_GRAVITY_RADIUS * 0.5D, 1.0D, CRUSHING_GRAVITY_RADIUS * 0.5D, 0.0D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startRoyalAcceleration() {
|
||||||
|
this.accelerationCooldownTicks = ACCELERATION_COOLDOWN;
|
||||||
|
this.royalEffects.start("acceleration", ACCELERATION_DURATION, () -> {
|
||||||
|
this.setRoyalAccelerated(true);
|
||||||
|
this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.4F);
|
||||||
|
applyAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
|
||||||
|
applyAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
|
||||||
|
}, this::tickRoyalAccelerationEffect, () -> {
|
||||||
|
this.setRoyalAccelerated(false);
|
||||||
|
removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
|
||||||
|
removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyAccelModifier(AttributeInstance attribute, ResourceLocation id) {
|
||||||
|
UUID uuid = UUID.nameUUIDFromBytes(id.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
if (attribute == null || attribute.getModifier(uuid) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
attribute.addTransientModifier(new AttributeModifier(uuid, id.toString(), 0.9D, AttributeModifier.Operation.MULTIPLY_BASE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeAccelModifier(AttributeInstance attribute, ResourceLocation id) {
|
||||||
|
if (attribute != null) {
|
||||||
|
attribute.removeModifier(UUID.nameUUIDFromBytes(id.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tickRoyalAccelerationEffect() {
|
||||||
|
if (!this.isRoyalAccelerated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.level() instanceof ServerLevel level && this.tickCount % 2 == 0) {
|
||||||
|
level.sendParticles(ACCEL_DUST,
|
||||||
|
this.getX() + (this.random.nextDouble() - 0.5D) * this.getBbWidth(),
|
||||||
|
this.getY() + this.random.nextDouble() * this.getBbHeight(),
|
||||||
|
this.getZ() + (this.random.nextDouble() - 0.5D) * this.getBbWidth(),
|
||||||
|
6, 0.4D, 0.6D, 0.4D, 0.02D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void die(DamageSource damageSource) {
|
||||||
|
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
|
||||||
|
DimensionalTearEntity.discardQueenOwnedTears(level, this.getUUID());
|
||||||
|
}
|
||||||
|
this.royalEffects.clear();
|
||||||
|
this.frozenVelocities.clear();
|
||||||
|
removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
|
||||||
|
removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
|
||||||
|
super.die(damageSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean spawnManticore(ServerLevel serverLevel, LivingEntity target) {
|
||||||
|
double minimumDistance = Math.max(4.0D, this.getBbWidth() * 0.55D);
|
||||||
|
double maximumDistance = Math.max(minimumDistance + 1.0D, AntarchySettings.queenManticoreSummonRange());
|
||||||
|
for (int attempt = 0; attempt < POSITION_ATTEMPTS_PER_MANTICORE; attempt++) {
|
||||||
|
double angle = this.random.nextDouble() * Mth.TWO_PI;
|
||||||
|
double distance = Mth.lerp(this.random.nextDouble(), minimumDistance, maximumDistance);
|
||||||
|
double x = this.getX() + Math.cos(angle) * distance;
|
||||||
|
double y = this.getY() + (this.random.nextDouble() - 0.5D) * 2.0D;
|
||||||
|
double z = this.getZ() + Math.sin(angle) * distance;
|
||||||
|
ManticoreEntity manticore = AntarchyObjects.MANTICORE.get().create(serverLevel);
|
||||||
|
if (manticore == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
manticore.moveTo(x, y, z, this.random.nextFloat() * 360.0F, 0.0F);
|
||||||
|
BlockPos spawnPos = manticore.blockPosition();
|
||||||
|
if (!serverLevel.noCollision(manticore)
|
||||||
|
|| !serverLevel.isEmptyBlock(spawnPos)
|
||||||
|
|| !ManticoreEntity.canSpawn(AntarchyObjects.MANTICORE.get(), serverLevel, MobSpawnType.MOB_SUMMONED, spawnPos, this.random)) {
|
||||||
|
manticore.discard();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
manticore.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(spawnPos), MobSpawnType.MOB_SUMMONED, null, null);
|
||||||
|
manticore.markQueenSummoned(this.getUUID());
|
||||||
|
manticore.setTarget(target);
|
||||||
|
serverLevel.addFreshEntity(manticore);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.addAdditionalSaveData(tag);
|
||||||
|
tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks);
|
||||||
|
tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks);
|
||||||
|
tag.putInt("TimeFieldCooldownTicks", this.timeFieldCooldownTicks);
|
||||||
|
tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks);
|
||||||
|
tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.readAdditionalSaveData(tag);
|
||||||
|
this.manticoreSummonCooldownTicks = Math.max(0, tag.getInt(SUMMON_COOLDOWN_KEY));
|
||||||
|
this.gravityStompCooldownTicks = Math.max(0, tag.getInt("GravityStompCooldownTicks"));
|
||||||
|
this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldownTicks"));
|
||||||
|
this.momentumLockCooldownTicks = Math.max(0, tag.getInt("MomentumLockCooldownTicks"));
|
||||||
|
this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks"));
|
||||||
|
this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks"));
|
||||||
|
this.blackHoleCooldownTicks = Math.max(0, tag.getInt("BlackHoleCooldownTicks"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public final class RoyalAssailantBlackHoleEntity extends RoyalBlackHoleEntity {
|
||||||
|
public RoyalAssailantBlackHoleEntity(EntityType<? extends RoyalAssailantBlackHoleEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RoyalAssailantBlackHoleEntity create(ServerLevel level, Vec3 center, @Nullable UUID ownerId) {
|
||||||
|
RoyalAssailantBlackHoleEntity hole = new RoyalAssailantBlackHoleEntity(
|
||||||
|
AntarchyObjects.ROYAL_ASSAILANT_BLACK_HOLE.get(), level);
|
||||||
|
hole.setPos(center.x, center.y, center.z);
|
||||||
|
hole.setRadius(AntarchySettings.queenBlackHoleRadius());
|
||||||
|
hole.setActiveTicks(AntarchySettings.queenBlackHoleActiveTicks());
|
||||||
|
hole.setOwnerId(ownerId);
|
||||||
|
return hole;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean risesWhileActive() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double timeDilationRadius() {
|
||||||
|
return Math.max(2.0D, this.radius() * 0.55D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean hasVisibleTimeDilationField() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int collapseDurationTicks() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean pullsTerrainBlocks() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+268
@@ -0,0 +1,268 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||||
|
import com.craisinlord.antarchy.content.time.TimeDilationApi;
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import software.bernie.geckolib.animatable.GeoEntity;
|
||||||
|
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimatableManager;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationController;
|
||||||
|
import software.bernie.geckolib.core.animation.AnimationState;
|
||||||
|
import software.bernie.geckolib.core.object.PlayState;
|
||||||
|
import software.bernie.geckolib.core.animation.RawAnimation;
|
||||||
|
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||||
|
|
||||||
|
public class RoyalBlackHoleEntity extends Entity implements GeoEntity {
|
||||||
|
private static final EntityDataAccessor<Float> RADIUS =
|
||||||
|
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT);
|
||||||
|
private static final EntityDataAccessor<Boolean> COLLAPSING =
|
||||||
|
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.BOOLEAN);
|
||||||
|
|
||||||
|
private static final String OWNER_KEY = "OwnerUuid";
|
||||||
|
private static final String AGE_KEY = "Age";
|
||||||
|
private static final String ACTIVE_KEY = "ActiveTicks";
|
||||||
|
|
||||||
|
private static final double DEFAULT_RADIUS = 14.0D;
|
||||||
|
private static final int DEFAULT_ACTIVE_TICKS = 120;
|
||||||
|
private static final int COLLAPSE_TICKS = 12;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private UUID ownerId;
|
||||||
|
private int age;
|
||||||
|
private int activeTicks = DEFAULT_ACTIVE_TICKS;
|
||||||
|
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||||
|
|
||||||
|
public RoyalBlackHoleEntity(EntityType<? extends RoyalBlackHoleEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
this.noPhysics = true;
|
||||||
|
this.noCulling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, double radius, int activeTicks, @Nullable UUID ownerId) {
|
||||||
|
RoyalBlackHoleEntity hole = new RoyalBlackHoleEntity(AntarchyObjects.ROYAL_BLACK_HOLE.get(), level);
|
||||||
|
hole.setPos(center.x, center.y, center.z);
|
||||||
|
hole.setRadius(radius > 0.0D ? radius : AntarchySettings.queenBlackHoleRadius());
|
||||||
|
hole.activeTicks = activeTicks > 0 ? Math.max(20, activeTicks) : AntarchySettings.queenBlackHoleActiveTicks();
|
||||||
|
hole.ownerId = ownerId;
|
||||||
|
return hole;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, @Nullable UUID ownerId) {
|
||||||
|
return create(level, center, AntarchySettings.queenBlackHoleRadius(), AntarchySettings.queenBlackHoleActiveTicks(), ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setActiveTicks(int activeTicks) {
|
||||||
|
this.activeTicks = activeTicks > 0 ? Math.max(20, activeTicks) : DEFAULT_ACTIVE_TICKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setOwnerId(@Nullable UUID ownerId) {
|
||||||
|
this.ownerId = ownerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean risesWhileActive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double timeDilationRadius() {
|
||||||
|
return Math.max(2.0D, this.radius() * 0.55D);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasVisibleTimeDilationField() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int collapseDurationTicks() {
|
||||||
|
return COLLAPSE_TICKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean pullsTerrainBlocks() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void defineSynchedData() {
|
||||||
|
this.entityData.define(RADIUS, (float) DEFAULT_RADIUS);
|
||||||
|
this.entityData.define(COLLAPSING, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRadius(double radius) {
|
||||||
|
this.entityData.set(RADIUS, (float) Math.max(1.0D, radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
public double radius() {
|
||||||
|
return this.entityData.get(RADIUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCollapsing() {
|
||||||
|
return this.entityData.get(COLLAPSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (this.level().isClientSide) {
|
||||||
|
this.spawnClientParticles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.age++;
|
||||||
|
if (!this.isCollapsing()) {
|
||||||
|
if (this.risesWhileActive()) {
|
||||||
|
this.setPos(this.getX(), this.getY() + 0.035D, this.getZ());
|
||||||
|
}
|
||||||
|
this.applyPull();
|
||||||
|
if (this.age % 10 == 0 && this.level() instanceof ServerLevel level) {
|
||||||
|
if (this.hasVisibleTimeDilationField()) {
|
||||||
|
TimeDilationApi.createField(level, this.position(), this.timeDilationRadius(), 0.18D, 14, this.ownerId);
|
||||||
|
}
|
||||||
|
if (this.pullsTerrainBlocks() && this.age % 20 == 0 && level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
||||||
|
RoyalBlockDestruction.destroySphere(level, this, this.position(), Math.min(2.0D, this.radius() * 0.2D), 8, 30.0D, 0.0F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.age >= this.activeTicks) {
|
||||||
|
this.entityData.set(COLLAPSING, true);
|
||||||
|
this.age = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.age >= this.collapseDurationTicks()) {
|
||||||
|
this.collapse();
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyPull() {
|
||||||
|
Vec3 center = this.position();
|
||||||
|
double radius = this.radius();
|
||||||
|
for (Entity entity : this.level().getEntitiesOfClass(Entity.class, new AABB(center, center).inflate(radius))) {
|
||||||
|
if (entity == this || this.isImmune(entity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Vec3 toCenter = center.subtract(entity.position());
|
||||||
|
double distance = toCenter.length();
|
||||||
|
if (distance < 0.5D || distance > radius) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double strength = AntarchySettings.queenBlackHolePullStrength() * (1.0D - distance / radius);
|
||||||
|
Vec3 pull = toCenter.scale(strength / distance);
|
||||||
|
entity.setDeltaMovement(entity.getDeltaMovement().scale(0.86D).add(pull));
|
||||||
|
entity.hasImpulse = true;
|
||||||
|
entity.fallDistance = 0.0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collapse() {
|
||||||
|
if (!(this.level() instanceof ServerLevel level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vec3 center = this.position();
|
||||||
|
double radius = this.radius();
|
||||||
|
DamageSource source = this.ownerEntity() != null
|
||||||
|
? this.damageSources().mobAttack(this.ownerEntity())
|
||||||
|
: this.damageSources().magic();
|
||||||
|
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(radius))) {
|
||||||
|
if (this.isImmune(living)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double distance = Math.max(1.0D, living.position().distanceTo(center));
|
||||||
|
float falloff = (float) Math.max(0.25D, 1.0D - distance / radius);
|
||||||
|
living.hurt(source, 26.0F * falloff);
|
||||||
|
Vec3 launch = living.position().subtract(center).normalize().scale(2.6D * falloff);
|
||||||
|
living.setDeltaMovement(launch.x, 1.1D * falloff + 0.4D, launch.z);
|
||||||
|
living.hasImpulse = true;
|
||||||
|
}
|
||||||
|
if (level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
||||||
|
RoyalBlockDestruction.destroySphere(level, this, center, radius * 0.4D, 160, 60.0D, 0.1F);
|
||||||
|
}
|
||||||
|
level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, center.x, center.y, center.z, 8, 3.0D, 3.0D, 3.0D, 0.0D);
|
||||||
|
level.sendParticles(ParticleTypes.REVERSE_PORTAL, center.x, center.y, center.z, 120, 4.0D, 4.0D, 4.0D, 0.6D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isImmune(Entity entity) {
|
||||||
|
if (entity instanceof RoyalBossEntity) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.ownerId != null && this.ownerId.equals(entity.getUUID())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private LivingEntity ownerEntity() {
|
||||||
|
if (this.ownerId == null || !(this.level() instanceof ServerLevel level)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return level.getEntity(this.ownerId) instanceof LivingEntity living ? living : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void spawnClientParticles() {
|
||||||
|
Vec3 center = this.position();
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
double angle = this.random.nextDouble() * Math.PI * 2.0D;
|
||||||
|
double r = this.radius() * (0.3D + this.random.nextDouble() * 0.7D);
|
||||||
|
double px = center.x + Math.cos(angle) * r;
|
||||||
|
double pz = center.z + Math.sin(angle) * r;
|
||||||
|
double py = center.y + (this.random.nextDouble() - 0.5D) * 3.0D;
|
||||||
|
this.level().addParticle(ParticleTypes.PORTAL, px, py, pz,
|
||||||
|
(center.x - px) * 0.18D, (center.y - py) * 0.18D, (center.z - pz) * 0.18D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPickable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isIgnoringBlockTriggers() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readAdditionalSaveData(CompoundTag tag) {
|
||||||
|
this.setRadius(tag.getDouble("Radius"));
|
||||||
|
this.age = tag.getInt(AGE_KEY);
|
||||||
|
this.activeTicks = tag.contains(ACTIVE_KEY) ? tag.getInt(ACTIVE_KEY) : DEFAULT_ACTIVE_TICKS;
|
||||||
|
this.ownerId = tag.hasUUID(OWNER_KEY) ? tag.getUUID(OWNER_KEY) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addAdditionalSaveData(CompoundTag tag) {
|
||||||
|
tag.putDouble("Radius", this.radius());
|
||||||
|
tag.putInt(AGE_KEY, this.age);
|
||||||
|
tag.putInt(ACTIVE_KEY, this.activeTicks);
|
||||||
|
if (this.ownerId != null) {
|
||||||
|
tag.putUUID(OWNER_KEY, this.ownerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||||
|
controllers.add(new AnimationController<>(this, "black_hole_controller", 0, this::blackHoleController));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayState blackHoleController(AnimationState<RoyalBlackHoleEntity> state) {
|
||||||
|
return state.setAndContinue(RawAnimation.begin().thenLoop("animation"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||||
|
return this.geoCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
public final class RoyalBlockDestruction {
|
||||||
|
private RoyalBlockDestruction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int destroySphere(
|
||||||
|
ServerLevel level,
|
||||||
|
Entity source,
|
||||||
|
Vec3 center,
|
||||||
|
double radius,
|
||||||
|
int maxBlocks,
|
||||||
|
double maxResistance,
|
||||||
|
float dropChance
|
||||||
|
) {
|
||||||
|
if (maxBlocks <= 0 || radius <= 0.0D || !level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minX = net.minecraft.util.Mth.floor(center.x - radius);
|
||||||
|
int minY = net.minecraft.util.Mth.floor(center.y - radius);
|
||||||
|
int minZ = net.minecraft.util.Mth.floor(center.z - radius);
|
||||||
|
int maxX = net.minecraft.util.Mth.floor(center.x + radius);
|
||||||
|
int maxY = net.minecraft.util.Mth.floor(center.y + radius);
|
||||||
|
int maxZ = net.minecraft.util.Mth.floor(center.z + radius);
|
||||||
|
double radiusSqr = radius * radius;
|
||||||
|
|
||||||
|
int destroyed = 0;
|
||||||
|
for (BlockPos cursor : BlockPos.betweenClosed(minX, minY, minZ, maxX, maxY, maxZ)) {
|
||||||
|
if (destroyed >= maxBlocks) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cursor.distToCenterSqr(center.x, center.y, center.z) > radiusSqr
|
||||||
|
|| cursor.getY() < level.getMinBuildHeight()
|
||||||
|
|| cursor.getY() >= level.getMaxBuildHeight()
|
||||||
|
|| !level.getWorldBorder().isWithinBounds(cursor)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos pos = cursor.immutable();
|
||||||
|
BlockState existing = level.getBlockState(pos);
|
||||||
|
if (existing.isAir()
|
||||||
|
|| level.getBlockEntity(pos) != null
|
||||||
|
|| existing.getDestroySpeed(level, pos) < 0.0F
|
||||||
|
|| existing.getBlock().getExplosionResistance() > maxResistance
|
||||||
|
|| existing.getCollisionShape(level, pos).isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level.random.nextFloat() <= dropChance) {
|
||||||
|
level.destroyBlock(pos, true, source);
|
||||||
|
} else {
|
||||||
|
level.levelEvent(2001, pos, Block.getId(existing));
|
||||||
|
level.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState());
|
||||||
|
}
|
||||||
|
destroyed++;
|
||||||
|
}
|
||||||
|
return destroyed;
|
||||||
|
}
|
||||||
|
}
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
package com.craisinlord.antarchy.content.entity.royal;
|
||||||
|
|
||||||
|
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||||
|
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
|
||||||
|
import net.minecraft.core.particles.ParticleOptions;
|
||||||
|
import net.minecraft.core.particles.ParticleTypes;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LightningBolt;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.EntityHitResult;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
|
|
||||||
|
public class RoyalBoltEntity extends AbstractHurtingProjectile {
|
||||||
|
private static final EntityDataAccessor<Integer> ELEMENT =
|
||||||
|
SynchedEntityData.defineId(RoyalBoltEntity.class, EntityDataSerializers.INT);
|
||||||
|
|
||||||
|
public RoyalBoltEntity(EntityType<? extends RoyalBoltEntity> entityType, Level level) {
|
||||||
|
super(entityType, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void defineSynchedData() {
|
||||||
|
super.defineSynchedData();
|
||||||
|
this.entityData.define(ELEMENT, RoyalBeamElement.GENERIC.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElement(RoyalBeamElement element) {
|
||||||
|
this.entityData.set(ELEMENT, element.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoyalBeamElement getElement() {
|
||||||
|
RoyalBeamElement[] values = RoyalBeamElement.values();
|
||||||
|
return values[Math.floorMod(this.entityData.get(ELEMENT), values.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.addAdditionalSaveData(tag);
|
||||||
|
tag.putInt("Element", this.entityData.get(ELEMENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readAdditionalSaveData(CompoundTag tag) {
|
||||||
|
super.readAdditionalSaveData(tag);
|
||||||
|
if (tag.contains("Element")) {
|
||||||
|
this.entityData.set(ELEMENT, tag.getInt("Element"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double getDefaultGravity() {
|
||||||
|
return 0.02D;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getInertia() {
|
||||||
|
return 0.97F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldBurn() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParticleOptions getTrailParticle() {
|
||||||
|
return switch (this.getElement()) {
|
||||||
|
case FIRE -> ParticleTypes.FLAME;
|
||||||
|
case LIGHTNING -> ParticleTypes.ELECTRIC_SPARK;
|
||||||
|
case ICE -> ParticleTypes.SNOWFLAKE;
|
||||||
|
case GENERIC -> ParticleTypes.WITCH;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHitEntity(EntityHitResult result) {
|
||||||
|
super.onHitEntity(result);
|
||||||
|
if (this.level().isClientSide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entity target = result.getEntity();
|
||||||
|
Entity owner = this.getOwner();
|
||||||
|
if (target == owner || (owner != null && target == owner.getVehicle())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float damage = (float) AntarchySettings.royalBoltDamage();
|
||||||
|
target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage);
|
||||||
|
if (target instanceof LivingEntity living) {
|
||||||
|
living.knockback(0.35D, this.getX() - target.getX(), this.getZ() - target.getZ());
|
||||||
|
switch (this.getElement()) {
|
||||||
|
case FIRE -> living.setRemainingFireTicks(Math.max(living.getRemainingFireTicks(), 80));
|
||||||
|
case ICE -> {
|
||||||
|
living.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 100, 1));
|
||||||
|
living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 40, living.getTicksFrozen() + 140));
|
||||||
|
}
|
||||||
|
case LIGHTNING -> {
|
||||||
|
if (this.level() instanceof ServerLevel serverLevel) {
|
||||||
|
LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(serverLevel);
|
||||||
|
if (bolt != null) {
|
||||||
|
bolt.moveTo(target.getX(), target.getY(), target.getZ());
|
||||||
|
bolt.setVisualOnly(true);
|
||||||
|
if (owner instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
|
||||||
|
bolt.setCause(serverPlayer);
|
||||||
|
}
|
||||||
|
serverLevel.addFreshEntity(bolt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case GENERIC -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHit(HitResult result) {
|
||||||
|
super.onHit(result);
|
||||||
|
if (!this.level().isClientSide) {
|
||||||
|
this.level().broadcastEntityEvent(this, (byte) 3);
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPickable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 3734 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user