Added first VERY rough draft of jumpy bug, creeping horror, lurking terror

This commit is contained in:
craisinlord committed 2026-06-24 23:03:09 -05:00
1 parent 70e4479f19
commit fb8fd3c927
112 files changed
+16339 -8

No files matched your search

@@ -271,6 +271,10 @@ public final class AntarchySettings {
private static double bedBugAttackLungeHorizontalSpeed = 0.42D;
private static double bedBugAttackLungeVerticalSpeed = 0.1D;
private static float bedBugHealAmount = 2.0F;
private static double jumpyBugHealth = 12.0D;
private static double jumpyBugPounceDamage = 2.0D;
private static double jumpyBugLatchDamage = 1.0D;
private static double jumpyBugCamouflageAlpha = 0.18D;
private static double brutalflyArmor = 10.0D;
private static double brutalflyArmorToughness = 4.0D;
private static double brutalflyKnockbackResistance = 0.7D;
@@ -451,11 +455,16 @@ public final class AntarchySettings {
private static double toreterrorSpinDamage = 8.0D;
private static double toreterrorSpinKnockback = 1.5D;
private static double toreterrorRangedWaterBombChance = 0.5D;
private static double toreterrorProjectileDamageMultiplier = 0.5D;
private static double waterBombDamage = 6.0D;
private static int waterBombLifetimeTicks = 120;
private static double waterBombGravity = 0.12D;
private static double waterBombKnockback = 1.2D;
private static double waterCannonCooldownSeconds = 1.5D;
private static double creepingHorrorHealth = 15.0D;
private static double creepingHorrorAttackDamage = 6.0D;
private static double lurkingTerrorHealth = 15.0D;
private static double lurkingTerrorAttackDamage = 6.0D;
private AntarchySettings() {
}
@@ -1811,6 +1820,18 @@ public final class AntarchySettings {
public static float bedBugHealAmount() { return bedBugHealAmount; }
public static void setBedBugHealAmount(float value) { bedBugHealAmount = value; }
public static double jumpyBugHealth() { return jumpyBugHealth; }
public static void setJumpyBugHealth(double value) { jumpyBugHealth = value; }
public static double jumpyBugPounceDamage() { return jumpyBugPounceDamage; }
public static void setJumpyBugPounceDamage(double value) { jumpyBugPounceDamage = value; }
public static double jumpyBugLatchDamage() { return jumpyBugLatchDamage; }
public static void setJumpyBugLatchDamage(double value) { jumpyBugLatchDamage = value; }
public static double jumpyBugCamouflageAlpha() { return jumpyBugCamouflageAlpha; }
public static void setJumpyBugCamouflageAlpha(double value) { jumpyBugCamouflageAlpha = value; }
public static double brutalflyArmor() { return brutalflyArmor; }
public static void setBrutalflyArmor(double value) { brutalflyArmor = value; }
@@ -2334,6 +2355,8 @@ public final class AntarchySettings {
public static void setToreterrorSpinKnockback(double v) { toreterrorSpinKnockback = v; }
public static double toreterrorRangedWaterBombChance() { return toreterrorRangedWaterBombChance; }
public static void setToreterrorRangedWaterBombChance(double v) { toreterrorRangedWaterBombChance = v; }
public static double toreterrorProjectileDamageMultiplier() { return toreterrorProjectileDamageMultiplier; }
public static void setToreterrorProjectileDamageMultiplier(double v) { toreterrorProjectileDamageMultiplier = v; }
public static double waterBombDamage() { return waterBombDamage; }
public static void setWaterBombDamage(double v) { waterBombDamage = v; }
public static int waterBombLifetimeTicks() { return waterBombLifetimeTicks; }
@@ -2344,4 +2367,13 @@ public final class AntarchySettings {
public static void setWaterBombKnockback(double v) { waterBombKnockback = v; }
public static double waterCannonCooldownSeconds() { return waterCannonCooldownSeconds; }
public static void setWaterCannonCooldownSeconds(double v) { waterCannonCooldownSeconds = v; }
public static double creepingHorrorHealth() { return creepingHorrorHealth; }
public static void setCreepingHorrorHealth(double v) { creepingHorrorHealth = v; }
public static double creepingHorrorAttackDamage() { return creepingHorrorAttackDamage; }
public static void setCreepingHorrorAttackDamage(double v) { creepingHorrorAttackDamage = v; }
public static double lurkingTerrorHealth() { return lurkingTerrorHealth; }
public static void setLurkingTerrorHealth(double v) { lurkingTerrorHealth = v; }
public static double lurkingTerrorAttackDamage() { return lurkingTerrorAttackDamage; }
public static void setLurkingTerrorAttackDamage(double v) { lurkingTerrorAttackDamage = v; }
}
@@ -8,6 +8,8 @@ import com.craisinlord.antarchy.content.entity.EasterBunnyEntity;
import com.craisinlord.antarchy.content.entity.HushProjectileEntity;
import com.craisinlord.antarchy.content.entity.WaterBombEntity;
import com.craisinlord.antarchy.content.entity.TriffidEntity;
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
import com.craisinlord.antarchy.content.entity.ToreterrorEntity;
import com.craisinlord.antarchy.content.entity.WaspEntity;
import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyEntity;
@@ -79,6 +81,10 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<EntityType<WaterBombEntity>> WATER_BOMB = (Supplier<EntityType<WaterBombEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<CreepingHorrorEntity>> CREEPING_HORROR = (Supplier<EntityType<CreepingHorrorEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<LurkingTerrorEntity>> LURKING_TERROR = (Supplier<EntityType<LurkingTerrorEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> DUPLICATOR_LOG = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> DUPLICATOR_SAPLING = (Supplier<Block>) UNBOUND;
@@ -185,6 +191,10 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<Block> BED_BUG_EGG = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> CREEPING_HORROR_EGGS = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> LURKING_TERROR_EGGS = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> WASP_NEST = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> HUSHWEED = (Supplier<Block>) UNBOUND;
@@ -222,6 +232,8 @@ public final class AntarchyObjects {
Supplier<EntityType<HushProjectileEntity>> hushProjectile,
Supplier<EntityType<ToreterrorEntity>> toreterror,
Supplier<EntityType<WaterBombEntity>> waterBomb,
Supplier<EntityType<CreepingHorrorEntity>> creepingHorror,
Supplier<EntityType<LurkingTerrorEntity>> lurkingTerror,
Supplier<Block> duplicatorLog,
Supplier<Block> duplicatorSapling,
Supplier<Block> ductTape,
@@ -272,6 +284,8 @@ public final class AntarchyObjects {
Supplier<Block> pinkMilkweed,
Supplier<Block> torchflowerBush,
Supplier<Block> bedBugEggs,
Supplier<Block> creepingHorrorEggs,
Supplier<Block> lurkingTerrorEggs,
Supplier<Block> waspNest,
Supplier<Block> hushweed,
Supplier<Block> squirrelNestBlock,
@@ -302,6 +316,8 @@ public final class AntarchyObjects {
HUSH_PROJECTILE = hushProjectile;
TORETERROR = toreterror;
WATER_BOMB = waterBomb;
CREEPING_HORROR = creepingHorror;
LURKING_TERROR = lurkingTerror;
DUPLICATOR_LOG = duplicatorLog;
DUPLICATOR_SAPLING = duplicatorSapling;
DUCT_TAPE = ductTape;
@@ -352,6 +368,8 @@ public final class AntarchyObjects {
PINK_MILKWEED = pinkMilkweed;
TORCHFLOWER_BUSH = torchflowerBush;
BED_BUG_EGG = bedBugEggs;
CREEPING_HORROR_EGGS = creepingHorrorEggs;
LURKING_TERROR_EGGS = lurkingTerrorEggs;
WASP_NEST = waspNest;
HUSHWEED = hushweed;
SQUIRREL_NEST_BLOCK = squirrelNestBlock;
@@ -0,0 +1,190 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ambient.Bat;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
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.entity.BlockEntity;
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.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class CreepingHorrorEggBlock extends Block {
public static final MapCodec<CreepingHorrorEggBlock> CODEC = Block.simpleCodec(CreepingHorrorEggBlock::new);
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
private static final int MAX_HATCH = 2;
private static final int MAX_EGGS = 4;
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
public CreepingHorrorEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
}
@Override
public MapCodec<CreepingHorrorEggBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
@Override
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
@Override
protected boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
return !context.isSecondaryUseActive()
&& context.getItemInHand().is(this.asItem())
&& state.getValue(EGGS) < MAX_EGGS;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
if (existingState.is(this)) {
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
}
return this.defaultBlockState();
}
@Override
protected boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level) || !hasHatchSupport(level, pos)) {
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.95F + random.nextFloat() * 0.1F);
return;
}
this.hatchEggs(level, pos, state, random);
}
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
if (!entity.isSteppingCarefully()) {
this.destroyEgg(level, state, pos, entity, 100);
}
super.stepOn(level, pos, state, entity);
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
this.destroyEgg(level, state, pos, entity, 3);
super.fallOn(level, state, pos, entity, fallDistance);
}
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
if (hasHatchSupport(level, pos) && !level.isClientSide) {
level.levelEvent(2012, pos, 15);
}
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
super.playerDestroy(level, player, pos, state, blockEntity, stack);
this.decreaseEggs(level, pos, state);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, EGGS);
}
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
return isSupportedGround(level, pos.below());
}
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
}
private void destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
return;
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
this.decreaseEggs(level, pos, state);
}
private void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
level.removeBlock(pos, false);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
for (int i = 0; i < state.getValue(EGGS); i++) {
CreepingHorrorEntity entity = AntarchyObjects.CREEPING_HORROR.get().create(level);
if (entity == null) {
continue;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
}
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
int eggs = state.getValue(EGGS);
if (eggs <= 1) {
level.destroyBlock(pos, false);
return;
}
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
}
private boolean shouldUpdateHatchLevel(Level level) {
return level.random.nextInt(500) == 0;
}
private boolean canDestroyEgg(Level level, Entity entity) {
if (entity instanceof CreepingHorrorEntity || entity instanceof Bat) {
return false;
}
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
return false;
}
}
@@ -0,0 +1,190 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ambient.Bat;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
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.entity.BlockEntity;
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.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class LurkingTerrorEggBlock extends Block {
public static final MapCodec<LurkingTerrorEggBlock> CODEC = Block.simpleCodec(LurkingTerrorEggBlock::new);
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
private static final int MAX_HATCH = 2;
private static final int MAX_EGGS = 4;
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
public LurkingTerrorEggBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
}
@Override
public MapCodec<LurkingTerrorEggBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
}
@Override
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
}
@Override
protected boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
return !context.isSecondaryUseActive()
&& context.getItemInHand().is(this.asItem())
&& state.getValue(EGGS) < MAX_EGGS;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
if (existingState.is(this)) {
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
}
return this.defaultBlockState();
}
@Override
protected boolean isRandomlyTicking(BlockState state) {
return true;
}
@Override
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (!this.shouldUpdateHatchLevel(level) || !hasHatchSupport(level, pos)) {
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.95F + random.nextFloat() * 0.1F);
return;
}
this.hatchEggs(level, pos, state, random);
}
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
if (!entity.isSteppingCarefully()) {
this.destroyEgg(level, state, pos, entity, 100);
}
super.stepOn(level, pos, state, entity);
}
@Override
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
this.destroyEgg(level, state, pos, entity, 3);
super.fallOn(level, state, pos, entity, fallDistance);
}
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
if (hasHatchSupport(level, pos) && !level.isClientSide) {
level.levelEvent(2012, pos, 15);
}
}
@Override
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
super.playerDestroy(level, player, pos, state, blockEntity, stack);
this.decreaseEggs(level, pos, state);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(HATCH, EGGS);
}
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
return isSupportedGround(level, pos.below());
}
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
}
private void destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
return;
}
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
this.decreaseEggs(level, pos, state);
}
private void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
level.removeBlock(pos, false);
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
for (int i = 0; i < state.getValue(EGGS); i++) {
LurkingTerrorEntity entity = AntarchyObjects.LURKING_TERROR.get().create(level);
if (entity == null) {
continue;
}
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
level.addFreshEntity(entity);
}
}
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
int eggs = state.getValue(EGGS);
if (eggs <= 1) {
level.destroyBlock(pos, false);
return;
}
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
}
private boolean shouldUpdateHatchLevel(Level level) {
return level.random.nextInt(500) == 0;
}
private boolean canDestroyEgg(Level level, Entity entity) {
if (entity instanceof LurkingTerrorEntity || entity instanceof Bat) {
return false;
}
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
}
return false;
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.content.client.model;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel;
public class CreepingHorrorModel extends GeoModel<CreepingHorrorEntity> {
@Override
public ResourceLocation getModelResource(CreepingHorrorEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/creeping_horror.geo.json");
}
@Override
public ResourceLocation getTextureResource(CreepingHorrorEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/creeping_horror.png");
}
@Override
public ResourceLocation getAnimationResource(CreepingHorrorEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/creeping_horror.animation.json");
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.content.client.model;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.JumpyBugEntity;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel;
public class JumpyBugModel extends GeoModel<JumpyBugEntity> {
@Override
public ResourceLocation getModelResource(JumpyBugEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/jumpy_bug.geo.json");
}
@Override
public ResourceLocation getTextureResource(JumpyBugEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/jumpy_bug.png");
}
@Override
public ResourceLocation getAnimationResource(JumpyBugEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/jumpy_bug.animation.json");
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.content.client.model;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel;
public class LurkingTerrorModel extends GeoModel<LurkingTerrorEntity> {
@Override
public ResourceLocation getModelResource(LurkingTerrorEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/lurking_terror.geo.json");
}
@Override
public ResourceLocation getTextureResource(LurkingTerrorEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/lurking_terror.png");
}
@Override
public ResourceLocation getAnimationResource(LurkingTerrorEntity animatable) {
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/lurking_terror.animation.json");
}
}
@@ -0,0 +1,28 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.model.CreepingHorrorModel;
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
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 org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class CreepingHorrorRenderer extends GeoEntityRenderer<CreepingHorrorEntity> {
public CreepingHorrorRenderer(EntityRendererProvider.Context context) {
super(context, new CreepingHorrorModel());
this.shadowRadius = 0.5F;
}
@Override
public void preRender(PoseStack poseStack, CreepingHorrorEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
if (animatable.isClimbing()) {
poseStack.mulPose(Axis.XP.rotationDegrees(-90.0F));
poseStack.translate(0, -0.5, 0.5);
}
}
}
@@ -0,0 +1,44 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.model.JumpyBugModel;
import com.craisinlord.antarchy.content.entity.JumpyBugEntity;
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.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;
import software.bernie.geckolib.util.Color;
public class JumpyBugRenderer extends GeoEntityRenderer<JumpyBugEntity> {
public JumpyBugRenderer(EntityRendererProvider.Context context) {
super(context, new JumpyBugModel());
this.shadowRadius = 3.0F;
}
@Override
public RenderType getRenderType(JumpyBugEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
return animatable.isCamouflaged() ? RenderType.entityTranslucent(texture) : RenderType.entityCutoutNoCull(texture);
}
@Override
public Color getRenderColor(JumpyBugEntity animatable, float partialTick, int packedLight) {
int alpha = Math.max(10, Math.min(255, Math.round(animatable.getVisualAlpha(partialTick) * 255.0F)));
return Color.ofRGBA(255, 255, 255, alpha);
}
@Override
public void preRender(PoseStack poseStack, JumpyBugEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
this.shadowRadius = animatable.isCamouflaged() ? 1.0F : 3.0F;
if (animatable.isClingingToCeiling()) {
poseStack.translate(0.0F, animatable.getBbHeight() + 0.05F, 0.0F);
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
}
}
}
@@ -0,0 +1,13 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.model.LurkingTerrorModel;
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class LurkingTerrorRenderer extends GeoEntityRenderer<LurkingTerrorEntity> {
public LurkingTerrorRenderer(EntityRendererProvider.Context context) {
super(context, new LurkingTerrorModel());
this.shadowRadius = 0.5F;
}
}
@@ -0,0 +1,130 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.config.AntarchySettings;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
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.WaterAvoidingRandomStrollGoal;
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 software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
public class CreepingHorrorEntity extends Monster implements GeoEntity {
private static final EntityDataAccessor<Boolean> CLIMBING =
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.BOOLEAN);
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenLoop("attack");
private static final byte ATTACK_ANIM_EVENT = 4;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private int attackAnimTicks = 0;
public CreepingHorrorEntity(EntityType<? extends CreepingHorrorEntity> type, Level level) {
super(type, level);
}
@Override
public float maxUpStep() {
return 1.0F;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder);
builder.define(CLIMBING, false);
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, AntarchySettings.creepingHorrorHealth())
.add(Attributes.MOVEMENT_SPEED, 0.30D)
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.creepingHorrorAttackDamage())
.add(Attributes.FOLLOW_RANGE, 16.0D);
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.85D));
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
@Override
public boolean onClimbable() {
return this.horizontalCollision;
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "main_controller", 3, this::mainAnimController));
}
private PlayState mainAnimController(AnimationState<CreepingHorrorEntity> state) {
if (attackAnimTicks > 0) {
return state.setAndContinue(ATTACK_ANIM);
}
if (state.isMoving()) {
return state.setAndContinue(WALK_ANIM);
}
return state.setAndContinue(IDLE_ANIM);
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return geoCache;
}
@Override
public boolean doHurtTarget(net.minecraft.world.entity.Entity target) {
boolean result = super.doHurtTarget(target);
if (result) {
attackAnimTicks = 20;
this.level().broadcastEntityEvent(this, ATTACK_ANIM_EVENT);
}
return result;
}
@Override
public void handleEntityEvent(byte id) {
if (id == ATTACK_ANIM_EVENT) {
attackAnimTicks = 20;
return;
}
super.handleEntityEvent(id);
}
@Override
public void tick() {
super.tick();
if (attackAnimTicks > 0) attackAnimTicks--;
}
//TODO IT NEVER ACTUALLY SETS TO CLIMBING
public boolean isClimbing() {
return this.entityData.get(CLIMBING);
}
}
@@ -0,0 +1,738 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
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.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.ByIdMap;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.Difficulty;
import net.minecraft.world.DifficultyInstance;
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.MobSpawnType;
import net.minecraft.world.entity.MoverType;
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.goal.FloatGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
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.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
import java.util.EnumSet;
import java.util.function.IntFunction;
public class JumpyBugEntity extends Monster implements GeoEntity {
private static final EntityDataAccessor<Boolean> CAMOUFLAGED =
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> CLINGING_TO_CEILING =
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Integer> LATCHED_TARGET_ID =
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> ATTACK_STATE =
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.INT);
private static final String POUNCE_COOLDOWN_KEY = "PounceCooldown";
private static final String LATCH_COOLDOWN_KEY = "LatchCooldown";
private static final String LATCH_TICKS_KEY = "LatchTicksRemaining";
private static final String BITE_COOLDOWN_KEY = "BiteCooldown";
private static final String RECOVERY_TICKS_KEY = "RecoveryTicks";
private static final String POUNCE_TICKS_KEY = "PounceTicks";
private static final double DEFAULT_MOVEMENT_SPEED = 0.25D;
private static final double DEFAULT_FOLLOW_RANGE = 16.0D;
private static final double DEFAULT_POUNCE_RANGE = 8.0D;
private static final int DEFAULT_LATCH_DURATION = 80;
private static final int DEFAULT_BITE_INTERVAL = 15;
private static final int DEFAULT_POUNCE_COOLDOWN = 60;
private static final int DEFAULT_LATCH_COOLDOWN = 80;
private static final int DEFAULT_RECOVERY_TICKS = 20;
private static final double POUNCE_HORIZONTAL_SPEED = 0.92D;
private static final double POUNCE_UPWARD_SPEED = 0.45D;
private static final double QUICK_MOVE_SQR = 0.14D * 0.14D;
private static final double CLING_SEARCH_RANGE = 1.5D;
private static final double LATCH_CHECK_RANGE = 0.85D;
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
private static final RawAnimation JUMP_ANIM = RawAnimation.begin().thenLoop("jump");
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack");
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private int pounceCooldownTicks;
private int latchCooldownTicks;
private int latchTicksRemaining;
private int biteCooldownTicks;
private int recoveryTicks;
private int pounceTicks;
private float visualAlpha = 1.0F;
private float previousVisualAlpha = 1.0F;
public JumpyBugEntity(EntityType<? extends JumpyBugEntity> entityType, Level level) {
super(entityType, level);
this.xpReward = 6;
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, AntarchySettings.jumpyBugHealth())
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.jumpyBugPounceDamage())
.add(Attributes.MOVEMENT_SPEED, DEFAULT_MOVEMENT_SPEED)
.add(Attributes.FOLLOW_RANGE, DEFAULT_FOLLOW_RANGE);
}
public static boolean canSpawn(EntityType<JumpyBugEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
return true;
}
return level.getDifficulty() != Difficulty.PEACEFUL
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder);
builder.define(CAMOUFLAGED, false);
builder.define(CLINGING_TO_CEILING, false);
builder.define(LATCHED_TARGET_ID, -1);
builder.define(ATTACK_STATE, AttackState.GROUND.id());
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new LatchedGoal());
this.goalSelector.addGoal(2, new PounceAttackGoal());
this.goalSelector.addGoal(3, new CeilingClingGoal());
this.goalSelector.addGoal(4, new ChaseTargetGoal());
this.goalSelector.addGoal(5, new WanderGoal());
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 10.0F));
this.goalSelector.addGoal(7, 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", 2, this::mainAnimController));
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache;
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putInt(POUNCE_COOLDOWN_KEY, this.pounceCooldownTicks);
tag.putInt(LATCH_COOLDOWN_KEY, this.latchCooldownTicks);
tag.putInt(LATCH_TICKS_KEY, this.latchTicksRemaining);
tag.putInt(BITE_COOLDOWN_KEY, this.biteCooldownTicks);
tag.putInt(RECOVERY_TICKS_KEY, this.recoveryTicks);
tag.putInt(POUNCE_TICKS_KEY, this.pounceTicks);
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.pounceCooldownTicks = tag.getInt(POUNCE_COOLDOWN_KEY);
this.latchCooldownTicks = tag.getInt(LATCH_COOLDOWN_KEY);
this.latchTicksRemaining = tag.getInt(LATCH_TICKS_KEY);
this.biteCooldownTicks = tag.getInt(BITE_COOLDOWN_KEY);
this.recoveryTicks = tag.getInt(RECOVERY_TICKS_KEY);
this.pounceTicks = tag.getInt(POUNCE_TICKS_KEY);
}
@Override
@Nullable
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData spawnGroupData) {
this.setHealth((float) AntarchySettings.jumpyBugHealth());
return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
}
@Override
public void tick() {
this.previousVisualAlpha = this.visualAlpha;
this.visualAlpha += (this.getTargetVisualAlpha() - this.visualAlpha) * 0.25F;
super.tick();
if (this.level().isClientSide) {
return;
}
if (this.pounceCooldownTicks > 0) {
this.pounceCooldownTicks--;
}
if (this.latchCooldownTicks > 0) {
this.latchCooldownTicks--;
}
if (this.biteCooldownTicks > 0) {
this.biteCooldownTicks--;
}
if (this.recoveryTicks > 0) {
this.recoveryTicks--;
}
if (this.isLatched()) {
this.tickLatched();
} else if (this.isClingingToCeiling()) {
this.tickClinging();
} else if (this.isPouncing()) {
this.tickPouncing();
} else {
this.setNoGravity(false);
this.noPhysics = false;
if (this.getAttackState() != AttackState.RECOVERING || this.recoveryTicks <= 0) {
this.setAttackState(AttackState.GROUND);
}
}
this.updateCamouflage();
}
@Override
public void travel(Vec3 travelVector) {
if (this.isLatched() || this.isClingingToCeiling()) {
this.setDeltaMovement(Vec3.ZERO);
return;
}
super.travel(travelVector);
}
@Override
public boolean hurt(DamageSource source, float amount) {
boolean hurt = super.hurt(source, amount);
if (!hurt) {
return false;
}
this.setCamouflaged(false);
this.recoveryTicks = Math.max(this.recoveryTicks, DEFAULT_RECOVERY_TICKS);
if (this.isLatched()) {
this.detachFromTarget(true);
}
if (this.isClingingToCeiling()) {
this.stopClinging();
}
if (this.isPouncing()) {
this.finishPounce(false);
}
return true;
}
@Override
public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
return false;
}
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.SPIDER_AMBIENT;
}
@Override
protected SoundEvent getHurtSound(DamageSource damageSource) {
return SoundEvents.SPIDER_HURT;
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.SPIDER_DEATH;
}
@Override
protected void playStepSound(BlockPos pos, BlockState blockState) {
this.playSound(SoundEvents.SPIDER_STEP, 0.12F, 1.1F);
}
public boolean isCamouflaged() {
return this.entityData.get(CAMOUFLAGED);
}
public boolean isClingingToCeiling() {
return this.entityData.get(CLINGING_TO_CEILING);
}
public boolean isLatched() {
return this.entityData.get(LATCHED_TARGET_ID) != -1;
}
public boolean isLatchedTo(LivingEntity entity) {
return this.entityData.get(LATCHED_TARGET_ID) == entity.getId();
}
public boolean isPouncing() {
return this.getAttackState() == AttackState.POUNCING;
}
public float getVisualAlpha(float partialTick) {
return Mth.lerp(partialTick, this.previousVisualAlpha, this.visualAlpha);
}
public AttackState getAttackState() {
return AttackState.BY_ID.apply(this.entityData.get(ATTACK_STATE));
}
private void setCamouflaged(boolean value) {
this.entityData.set(CAMOUFLAGED, value);
}
private void setClingingToCeiling(boolean value) {
this.entityData.set(CLINGING_TO_CEILING, value);
}
private void setAttackState(AttackState state) {
this.entityData.set(ATTACK_STATE, state.id());
}
private float getTargetVisualAlpha() {
return this.isCamouflaged() ? (float) AntarchySettings.jumpyBugCamouflageAlpha() : 1.0F;
}
private PlayState mainAnimController(AnimationState<JumpyBugEntity> state) {
AttackState attackState = this.getAttackState();
if (attackState == AttackState.POUNCING || attackState == AttackState.LATCHED || attackState == AttackState.CLINGING) {
return state.setAndContinue(JUMP_ANIM);
}
if (attackState == AttackState.RECOVERING) {
return state.setAndContinue(ATTACK_ANIM);
}
return state.setAndContinue(state.isMoving() ? WALK_ANIM : IDLE_ANIM);
}
private void tickClinging() {
BlockPos clingPos = this.findClingBlockPos();
if (clingPos == null) {
this.stopClinging();
return;
}
this.setAttackState(AttackState.CLINGING);
this.setCamouflaged(true);
this.setNoGravity(true);
this.noPhysics = true;
this.getNavigation().stop();
this.setDeltaMovement(Vec3.ZERO);
this.move(MoverType.SELF, Vec3.ZERO);
this.setPos(this.getX(), clingPos.getY() - this.getBbHeight() - 0.01D, this.getZ());
}
private void tickPouncing() {
this.setAttackState(AttackState.POUNCING);
this.setCamouflaged(false);
this.pounceTicks++;
LivingEntity target = this.getTarget();
if (target != null && target.isAlive() && this.distanceToSqr(target) <= (this.getBbWidth() + target.getBbWidth() + LATCH_CHECK_RANGE) * (this.getBbWidth() + target.getBbWidth() + LATCH_CHECK_RANGE)) {
if (this.tryLatch(target)) {
return;
}
}
if (this.onGround() && this.pounceTicks > 4) {
this.finishPounce(false);
}
}
private void tickLatched() {
LivingEntity target = this.getLatchedTarget();
if (target == null || !target.isAlive() || target.level() != this.level() || target.isRemoved() || target.isInWater()) {
this.detachFromTarget(false);
return;
}
this.setAttackState(AttackState.LATCHED);
this.setCamouflaged(false);
this.setNoGravity(true);
this.noPhysics = true;
this.getNavigation().stop();
this.setDeltaMovement(Vec3.ZERO);
Vec3 latchPos = this.getLatchPosition(target);
this.setPos(latchPos.x, latchPos.y, latchPos.z);
this.yRotO = this.getYRot();
this.setYRot(target.getYHeadRot());
this.setYBodyRot(target.getYHeadRot());
this.setYHeadRot(target.getYHeadRot());
this.setXRot(-20.0F);
if (--this.latchTicksRemaining <= 0) {
this.detachFromTarget(true);
return;
}
if (this.biteCooldownTicks <= 0) {
target.hurt(this.damageSources().mobAttack(this), (float) AntarchySettings.jumpyBugLatchDamage());
this.biteCooldownTicks = DEFAULT_BITE_INTERVAL;
this.playSound(SoundEvents.SPIDER_HURT, 0.35F, 1.5F + this.random.nextFloat() * 0.2F);
}
}
private void updateCamouflage() {
if (this.isLatched() || this.isPouncing()) {
this.setCamouflaged(false);
return;
}
boolean idle = this.getTarget() == null && this.getDeltaMovement().horizontalDistanceSqr() < 0.003D;
boolean stalking = this.isClingingToCeiling();
boolean movingQuickly = this.getDeltaMovement().lengthSqr() > QUICK_MOVE_SQR;
boolean shouldCamouflage = !movingQuickly && (idle || stalking);
this.setCamouflaged(shouldCamouflage);
}
private boolean tryLatch(LivingEntity target) {
if (!(target instanceof Player) || this.latchCooldownTicks > 0 || this.isLatched()) {
return false;
}
if (this.level().getEntitiesOfClass(JumpyBugEntity.class, target.getBoundingBox().inflate(0.75D),
bug -> bug != this && bug.isLatchedTo(target)).isEmpty()) {
this.entityData.set(LATCHED_TARGET_ID, target.getId());
this.latchTicksRemaining = DEFAULT_LATCH_DURATION;
this.biteCooldownTicks = 0;
this.pounceTicks = 0;
this.setAttackState(AttackState.LATCHED);
this.setTarget(target);
target.hurt(this.damageSources().mobAttack(this), (float) AntarchySettings.jumpyBugPounceDamage());
return true;
}
return false;
}
private void detachFromTarget(boolean knockAway) {
LivingEntity target = this.getLatchedTarget();
this.entityData.set(LATCHED_TARGET_ID, -1);
this.latchTicksRemaining = 0;
this.biteCooldownTicks = DEFAULT_BITE_INTERVAL;
this.latchCooldownTicks = DEFAULT_LATCH_COOLDOWN;
this.setAttackState(AttackState.RECOVERING);
this.recoveryTicks = DEFAULT_RECOVERY_TICKS;
this.setNoGravity(false);
this.noPhysics = false;
if (knockAway && target != null) {
Vec3 away = this.position().subtract(target.position());
if (away.lengthSqr() < 1.0E-4D) {
away = target.getLookAngle().scale(-1.0D);
}
away = away.normalize();
this.setDeltaMovement(away.x * 0.35D, 0.25D, away.z * 0.35D);
this.hasImpulse = true;
}
}
@Nullable
private LivingEntity getLatchedTarget() {
int entityId = this.entityData.get(LATCHED_TARGET_ID);
if (entityId == -1) {
return null;
}
Entity entity = this.level().getEntity(entityId);
return entity instanceof LivingEntity livingEntity ? livingEntity : null;
}
private Vec3 getLatchPosition(LivingEntity target) {
Vec3 eyePos = target.position().add(0.0D, target.getEyeHeight() - 0.15D, 0.0D);
Vec3 backOffset = target.getLookAngle().normalize().scale(-0.16D);
return eyePos.add(backOffset).add(0.0D, -0.18D, 0.0D);
}
@Nullable
private BlockPos findClingBlockPos() {
BlockPos origin = this.blockPosition();
for (int dy = 1; dy <= 2; dy++) {
BlockPos candidate = origin.above(dy);
if (this.isCeilingBlock(candidate)) {
return candidate;
}
}
return null;
}
private boolean isCeilingBlock(BlockPos pos) {
return this.level().getBlockState(pos).isFaceSturdy(this.level(), pos, net.minecraft.core.Direction.DOWN);
}
private boolean canClingHere() {
return this.findClingBlockPos() != null;
}
private void beginClinging() {
this.setClingingToCeiling(true);
this.setAttackState(AttackState.CLINGING);
this.setNoGravity(true);
this.noPhysics = true;
this.getNavigation().stop();
this.setDeltaMovement(Vec3.ZERO);
}
private void stopClinging() {
this.setClingingToCeiling(false);
this.setAttackState(AttackState.GROUND);
this.setNoGravity(false);
this.noPhysics = false;
}
private void beginPounce(LivingEntity target) {
this.stopClinging();
this.setTarget(target);
this.setAttackState(AttackState.POUNCING);
this.setCamouflaged(false);
this.getNavigation().stop();
this.pounceCooldownTicks = DEFAULT_POUNCE_COOLDOWN;
this.pounceTicks = 0;
Vec3 from = this.position();
Vec3 to = target.getEyePosition().subtract(from);
Vec3 horizontal = new Vec3(to.x, 0.0D, to.z);
Vec3 direction = horizontal.lengthSqr() > 1.0E-4D ? horizontal.normalize() : target.getLookAngle();
double upward = POUNCE_UPWARD_SPEED + Mth.clamp(to.y * 0.08D, -0.04D, 0.22D);
this.setDeltaMovement(direction.x * POUNCE_HORIZONTAL_SPEED, upward, direction.z * POUNCE_HORIZONTAL_SPEED);
this.hasImpulse = true;
}
private void finishPounce(boolean keepMomentum) {
this.pounceTicks = 0;
if (!keepMomentum) {
Vec3 movement = this.getDeltaMovement();
this.setDeltaMovement(movement.x * 0.25D, Math.max(0.0D, movement.y), movement.z * 0.25D);
}
this.setAttackState(AttackState.RECOVERING);
this.recoveryTicks = DEFAULT_RECOVERY_TICKS;
}
private boolean canUseAmbush() {
return !this.isLatched()
&& !this.isPouncing()
&& this.recoveryTicks <= 0
&& this.latchCooldownTicks <= 0;
}
private enum AttackState implements StringRepresentable {
GROUND(0, "ground"),
CLINGING(1, "clinging"),
POUNCING(2, "pouncing"),
LATCHED(3, "latched"),
RECOVERING(4, "recovering");
private static final IntFunction<AttackState> BY_ID = ByIdMap.continuous(AttackState::id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
public static final Codec<AttackState> CODEC = StringRepresentable.fromEnum(AttackState::values);
private final int id;
private final String name;
AttackState(int id, String name) {
this.id = id;
this.name = name;
}
public int id() {
return this.id;
}
@Override
public String getSerializedName() {
return this.name;
}
}
private final class LatchedGoal extends Goal {
private LatchedGoal() {
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
}
@Override
public boolean canUse() {
return JumpyBugEntity.this.isLatched();
}
@Override
public boolean canContinueToUse() {
return JumpyBugEntity.this.isLatched();
}
@Override
public void tick() {
JumpyBugEntity.this.getNavigation().stop();
}
}
private final class PounceAttackGoal extends Goal {
private PounceAttackGoal() {
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
}
@Override
public boolean canUse() {
LivingEntity target = JumpyBugEntity.this.getTarget();
return target != null
&& target.isAlive()
&& JumpyBugEntity.this.canUseAmbush()
&& JumpyBugEntity.this.pounceCooldownTicks <= 0
&& JumpyBugEntity.this.distanceToSqr(target) <= DEFAULT_POUNCE_RANGE * DEFAULT_POUNCE_RANGE
&& (JumpyBugEntity.this.hasLineOfSight(target) || JumpyBugEntity.this.distanceToSqr(target) <= 9.0D);
}
@Override
public boolean canContinueToUse() {
return JumpyBugEntity.this.isPouncing();
}
@Override
public void start() {
LivingEntity target = JumpyBugEntity.this.getTarget();
if (target != null) {
JumpyBugEntity.this.beginPounce(target);
}
}
}
private final class CeilingClingGoal extends Goal {
private CeilingClingGoal() {
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
}
@Override
public boolean canUse() {
if (!JumpyBugEntity.this.canUseAmbush() || JumpyBugEntity.this.isClingingToCeiling()) {
return false;
}
LivingEntity target = JumpyBugEntity.this.getTarget();
if (target == null) {
return JumpyBugEntity.this.random.nextInt(40) == 0 && JumpyBugEntity.this.canClingHere();
}
return JumpyBugEntity.this.distanceToSqr(target) <= (DEFAULT_POUNCE_RANGE + CLING_SEARCH_RANGE) * (DEFAULT_POUNCE_RANGE + CLING_SEARCH_RANGE)
&& JumpyBugEntity.this.canClingHere();
}
@Override
public boolean canContinueToUse() {
return JumpyBugEntity.this.isClingingToCeiling();
}
@Override
public void start() {
JumpyBugEntity.this.beginClinging();
}
@Override
public void stop() {
if (JumpyBugEntity.this.isClingingToCeiling()) {
JumpyBugEntity.this.stopClinging();
}
}
@Override
public void tick() {
LivingEntity target = JumpyBugEntity.this.getTarget();
if (target == null) {
return;
}
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
if (JumpyBugEntity.this.pounceCooldownTicks <= 0
&& JumpyBugEntity.this.distanceToSqr(target) <= DEFAULT_POUNCE_RANGE * DEFAULT_POUNCE_RANGE
&& (JumpyBugEntity.this.hasLineOfSight(target) || JumpyBugEntity.this.distanceToSqr(target) <= 4.0D)) {
JumpyBugEntity.this.stopClinging();
JumpyBugEntity.this.beginPounce(target);
}
}
}
private final class ChaseTargetGoal extends Goal {
private ChaseTargetGoal() {
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
}
@Override
public boolean canUse() {
LivingEntity target = JumpyBugEntity.this.getTarget();
return target != null
&& target.isAlive()
&& !JumpyBugEntity.this.isLatched()
&& !JumpyBugEntity.this.isClingingToCeiling()
&& !JumpyBugEntity.this.isPouncing()
&& JumpyBugEntity.this.recoveryTicks <= 0;
}
@Override
public boolean canContinueToUse() {
return this.canUse();
}
@Override
public void tick() {
LivingEntity target = JumpyBugEntity.this.getTarget();
if (target == null) {
return;
}
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
if (JumpyBugEntity.this.distanceToSqr(target) > DEFAULT_POUNCE_RANGE * DEFAULT_POUNCE_RANGE) {
JumpyBugEntity.this.getNavigation().moveTo(target, 1.05D);
} else {
JumpyBugEntity.this.getNavigation().stop();
}
}
}
private final class WanderGoal extends RandomStrollGoal {
private WanderGoal() {
super(JumpyBugEntity.this, 0.8D);
}
@Override
public boolean canUse() {
return !JumpyBugEntity.this.isLatched()
&& !JumpyBugEntity.this.isClingingToCeiling()
&& !JumpyBugEntity.this.isPouncing()
&& JumpyBugEntity.this.getTarget() == null
&& super.canUse();
}
@Override
public boolean canContinueToUse() {
return !JumpyBugEntity.this.isLatched()
&& !JumpyBugEntity.this.isClingingToCeiling()
&& !JumpyBugEntity.this.isPouncing()
&& super.canContinueToUse();
}
}
}
@@ -0,0 +1,126 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.config.AntarchySettings;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
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 software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
public class LurkingTerrorEntity extends Monster implements GeoEntity {
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
private static final RawAnimation ATTACK_LAND_ANIM = RawAnimation.begin().thenLoop("attackland");
private static final RawAnimation ATTACK_AIR_ANIM = RawAnimation.begin().thenLoop("attackair");
private static final byte ATTACK_ANIM_EVENT = 5;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private int attackAnimTicks = 0;
public LurkingTerrorEntity(EntityType<? extends LurkingTerrorEntity> type, Level level) {
super(type, level);
this.moveControl = new FlyingMoveControl(this, 10, true);
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, AntarchySettings.lurkingTerrorHealth())
.add(Attributes.MOVEMENT_SPEED, 0.25D)
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.lurkingTerrorAttackDamage())
.add(Attributes.FOLLOW_RANGE, 16.0D)
.add(Attributes.FLYING_SPEED, 0.4D);
}
@Override
protected PathNavigation createNavigation(Level level) {
FlyingPathNavigation nav = new FlyingPathNavigation(this, level);
nav.setCanOpenDoors(false);
nav.setCanFloat(true);
nav.setCanPassDoors(true);
return nav;
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.85D));
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(4, 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", 3, this::mainAnimController));
}
private PlayState mainAnimController(AnimationState<LurkingTerrorEntity> state) {
if (attackAnimTicks > 0) {
// if (!this.isOnGround()) {
// return state.setAndContinue(ATTACK_AIR_ANIM);
// }
return state.setAndContinue(ATTACK_LAND_ANIM);
}
if (state.isMoving()) {
return state.setAndContinue(WALK_ANIM);
}
return state.setAndContinue(IDLE_ANIM);
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return geoCache;
}
@Override
public boolean doHurtTarget(net.minecraft.world.entity.Entity target) {
boolean result = super.doHurtTarget(target);
if (result) {
attackAnimTicks = 20;
this.level().broadcastEntityEvent(this, ATTACK_ANIM_EVENT);
}
return result;
}
@Override
public void handleEntityEvent(byte id) {
if (id == ATTACK_ANIM_EVENT) {
attackAnimTicks = 20;
return;
}
super.handleEntityEvent(id);
}
@Override
public void tick() {
super.tick();
if (attackAnimTicks > 0) attackAnimTicks--;
}
@Override
public boolean causeFallDamage(float fallDistance, float multiplier, net.minecraft.world.damagesource.DamageSource source) {
return false;
}
}
@@ -16,6 +16,7 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.BossEvent;
import net.minecraft.world.Difficulty;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@@ -219,6 +220,10 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
@Override
public boolean hurt(DamageSource source, float amount) {
if (source.is(net.minecraft.world.damagesource.DamageTypes.DROWN)) return false;
if (source.is(DamageTypeTags.IS_PROJECTILE)) {
amount *= (float) AntarchySettings.toreterrorProjectileDamageMultiplier();
}
if (!this.isPhase2()) {
float halfHealth = this.getMaxHealth() * 0.5F;
if (this.getHealth() - amount <= halfHealth) {
@@ -0,0 +1,20 @@
package com.craisinlord.antarchy.content.item;
public final class JumpyBootsHelper {
public static final int CHARGE_TICKS_MAX = 30;
public static final int PRIME_WINDOW_TICKS = 15;
public static final int COOLDOWN_TICKS = 40;
public static final float MAX_VERTICAL_BOOST = 2.0F;
public static final float SPRINT_FORWARD_BOOST = 0.3F;
public static final long FALL_PROTECTION_TICKS = 120L;
public static final String FALL_PROTECTION_NBT_KEY = "antarchy:jumpy_boots_protection_until";
private JumpyBootsHelper() {
}
public static float verticalBoostFor(int chargeTicks) {
float ratio = Math.min(chargeTicks / (float) CHARGE_TICKS_MAX, 1.0F);
return ratio * MAX_VERTICAL_BOOST;
}
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.content.item;
import net.minecraft.ChatFormatting;
import net.minecraft.core.Holder;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.ArmorMaterial;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import java.util.List;
public final class JumpyBootsItem extends ArmorItem {
public JumpyBootsItem(Holder<ArmorMaterial> material, Properties properties) {
super(material, Type.BOOTS, properties);
}
public static boolean isWearingJumpyBoots(net.minecraft.world.entity.LivingEntity entity) {
return entity != null && entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET).getItem() instanceof JumpyBootsItem;
}
@Override
public boolean isEnchantable(ItemStack stack) {
return stack.getCount() == 1;
}
@Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(Component.translatable("tooltip.antarchy.jumpy_boots").withStyle(ChatFormatting.DARK_RED));
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
}
@@ -0,0 +1,24 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record JumpyBootsLaunchPayload(int chargeTicks, boolean sprinting) implements CustomPacketPayload {
public static final Type<JumpyBootsLaunchPayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "jumpy_boots_launch"));
public static final StreamCodec<ByteBuf, JumpyBootsLaunchPayload> STREAM_CODEC =
StreamCodec.composite(
ByteBufCodecs.VAR_INT, JumpyBootsLaunchPayload::chargeTicks,
ByteBufCodecs.BOOL, JumpyBootsLaunchPayload::sprinting,
JumpyBootsLaunchPayload::new
);
@Override
public Type<JumpyBootsLaunchPayload> type() {
return TYPE;
}
}