From 8cabdb33bff4ae6df2b52e9a18f03d92a157f9b2 Mon Sep 17 00:00:00 2001 From: craisinlord Date: Tue, 30 Jun 2026 14:02:16 -0500 Subject: [PATCH] first herc beetle implementation --- .../antarchy/config/AntarchySettings.java | 11 +- .../antarchy/content/AntarchyObjects.java | 7 + .../antarchy/content/AntarchyTags.java | 2 + .../antarchy/content/StinkyBehavior.java | 13 + .../HerculesBeetleImpactShakeClientState.java | 26 + .../client/model/HerculesBeetleModel.java | 28 + .../renderer/HerculesBeetleRenderer.java | 12 + .../content/client/screen/DorrieScreen.java | 40 - .../antarchy/content/entity/DorrieEntity.java | 240 ++-- .../content/entity/HerculesBeetleEntity.java | 1233 +++++++++++++++++ .../content/entity/StinkBugEntity.java | 20 +- .../content/entity/ToreterrorEntity.java | 23 + .../flying_squirrel/FlyingSquirrelEntity.java | 63 +- .../content/item/CheepOnAStickItem.java | 9 - .../antarchy/content/menu/DorrieMenu.java | 129 -- .../HerculesBeetleImpactShakePayload.java | 20 + .../HerculesBeetleImpactShakeSync.java | 22 + .../HerculesBeetleJumpInputPayload.java | 23 + .../HerculesBeetleMountedAttackPayload.java | 19 + .../HerculesBeetleMountedChargePayload.java | 20 + .../client/MountedVehicleCameraMixin.java | 49 + .../resources/antarchy-common.mixins.json | 1 + .../resources/assets/antarchy/lang/en_us.json | 5 +- .../item/hercules_beetle_spawn_egg.json | 3 + .../data/antarchy/dimension_type/cavaryn.json | 2 +- .../hercules_beetle_charge_immune_blocks.json | 18 + .../tags/item/hercules_beetle_food.json | 6 + .../worldgen/biome/butterfly_fields.json | 4 - .../data/antarchy/worldgen/biome/cavaryn.json | 13 +- .../minecraft/tags/entity_type/arthropod.json | 1 + .../fabric/AntarchyFabricContent.java | 15 +- .../client/AntarchyFabricClientBootstrap.java | 8 +- .../fabric/client/AntarchyKeyBindings.java | 12 + .../client/HerculesBeetleClientHandler.java | 66 + ...aShakeMixin.java => CameraShakeMixin.java} | 23 +- .../client/NightmareCameraShakeMixin.java | 63 - .../AntarchyFabricClientNetworking.java | 2 + .../network/AntarchyFabricNetworking.java | 35 + .../resources/antarchy-fabric.mixins.json | 2 +- .../AntarchyConfigModuleNeoforge.java | 7 + .../antarchy/neoforge/AntarchyMobsConfig.java | 16 + .../antarchy/neoforge/AntarchyNeoforge.java | 1 + .../neoforge/client/AntarchyKeyBindings.java | 9 + .../client/AntarchyNeoForgeClient.java | 1 + ...keHandler.java => CameraShakeHandler.java} | 23 +- .../client/HerculesBeetleClientHandler.java | 76 + .../network/HerculesBeetleNetworking.java | 73 + .../AntarchyNeoforgeCreativeModeTabs.java | 5 +- .../registry/AntarchyNeoforgeEntites.java | 6 + .../AntarchyNeoforgeEntityAttributes.java | 1 + .../registry/AntarchyNeoforgeItems.java | 5 +- .../AntarchyNeoforgePayloadHandlers.java | 2 + 52 files changed, 2102 insertions(+), 411 deletions(-) create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/client/HerculesBeetleImpactShakeClientState.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/client/model/HerculesBeetleModel.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/client/renderer/HerculesBeetleRenderer.java delete mode 100644 common/src/main/java/com/craisinlord/antarchy/content/client/screen/DorrieScreen.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/entity/HerculesBeetleEntity.java delete mode 100644 common/src/main/java/com/craisinlord/antarchy/content/item/CheepOnAStickItem.java delete mode 100644 common/src/main/java/com/craisinlord/antarchy/content/menu/DorrieMenu.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakePayload.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakeSync.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleJumpInputPayload.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedAttackPayload.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedChargePayload.java create mode 100644 common/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java create mode 100644 common/src/main/resources/assets/antarchy/models/item/hercules_beetle_spawn_egg.json create mode 100644 common/src/main/resources/data/antarchy/tags/block/hercules_beetle_charge_immune_blocks.json create mode 100644 common/src/main/resources/data/antarchy/tags/item/hercules_beetle_food.json create mode 100644 fabric/src/main/java/com/craisinlord/antarchy/fabric/client/HerculesBeetleClientHandler.java rename fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/{ToreterrorCameraShakeMixin.java => CameraShakeMixin.java} (64%) delete mode 100644 fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/NightmareCameraShakeMixin.java rename neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/{ToreterrorCameraShakeHandler.java => CameraShakeHandler.java} (64%) create mode 100644 neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/HerculesBeetleClientHandler.java create mode 100644 neoforge/src/main/java/com/craisinlord/antarchy/neoforge/network/HerculesBeetleNetworking.java diff --git a/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index 42bd45cf..4830cb70 100644 --- a/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -150,7 +150,7 @@ public final class AntarchySettings { private static int basiliskSpawnMaxLightLevel = 5; private static double basiliskHealth = 150.0D; private static double basiliskAttackDamage = 36.0D; - private static double basiliskMovementSpeed = 0.30D; + private static double basiliskMovementSpeed = 0.225D; private static double basiliskArmor = 6.0D; private static double basiliskKnockbackResistance = 0.5D; private static double basiliskFollowRange = 28.0D; @@ -470,6 +470,9 @@ public final class AntarchySettings { private static double cheepHealth = 8.0D; private static double cheepAttackDamage = 3.0D; private static double dorrieHealth = 60.0D; + private static double herculesBeetleHealth = 500.0D; + private static double herculesBeetleAttackDamage = 30.0D; + private static double herculesBeetleChargeDamage = 40.0D; private AntarchySettings() { } @@ -2398,5 +2401,11 @@ public final class AntarchySettings { public static double cheepAttackDamage() { return cheepAttackDamage; } public static void setCheepAttackDamage(double v) { cheepAttackDamage = v; } public static double dorrieHealth() { return dorrieHealth; } + public static double herculesBeetleHealth() { return herculesBeetleHealth; } + public static void setHerculesBeetleHealth(double v) { herculesBeetleHealth = v; } + public static double herculesBeetleAttackDamage() { return herculesBeetleAttackDamage; } + public static void setHerculesBeetleAttackDamage(double v) { herculesBeetleAttackDamage = v; } + public static double herculesBeetleChargeDamage() { return herculesBeetleChargeDamage; } + public static void setHerculesBeetleChargeDamage(double v) { herculesBeetleChargeDamage = v; } public static void setDorrieHealth(double v) { dorrieHealth = v; } } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java b/common/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java index e802be8c..64ce3fb2 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/AntarchyObjects.java @@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.entity.BomberEntity; import com.craisinlord.antarchy.content.entity.ButterflyEntity; import com.craisinlord.antarchy.content.entity.CaterpillarEntity; import com.craisinlord.antarchy.content.entity.EasterBunnyEntity; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; import com.craisinlord.antarchy.content.entity.HushProjectileEntity; import com.craisinlord.antarchy.content.entity.StinkBugEntity; import com.craisinlord.antarchy.content.entity.WaterBombEntity; @@ -96,6 +97,8 @@ public final class AntarchyObjects { @SuppressWarnings("unchecked") public static Supplier> DORRIE = (Supplier>) UNBOUND; @SuppressWarnings("unchecked") + public static Supplier> HERCULES_BEETLE = (Supplier>) UNBOUND; + @SuppressWarnings("unchecked") public static Supplier CHEEP_ITEM = (Supplier) UNBOUND; @SuppressWarnings("unchecked") public static Supplier DUPLICATOR_LOG = (Supplier) UNBOUND; @@ -433,6 +436,10 @@ public final class AntarchyObjects { OCTOPUS_BOMB = supplier; } + public static void setHerculesBeetle(Supplier> supplier) { + HERCULES_BEETLE = supplier; + } + public static BlockBehaviour.Properties shellstoneProperties() { return BlockBehaviour.Properties.ofFullCopy(Blocks.STONE).requiresCorrectToolForDrops(); } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java b/common/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java index d2f7ba3e..3aa23a25 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java @@ -30,6 +30,7 @@ public final class AntarchyTags { public static final TagKey MOLEVORE_SEE_THROUGH_BLOCKS = TagKey.create(Registries.BLOCK, id("molevore_see_through_blocks")); public static final TagKey TERMITE_FOODS = TagKey.create(Registries.BLOCK, id("termite_foods")); public static final TagKey UMBRAL_MOSS_REPLACEABLE = TagKey.create(Registries.BLOCK, id("umbral_moss_replaceable")); + public static final TagKey HERCULES_BEETLE_CHARGE_IMMUNE_BLOCKS = TagKey.create(Registries.BLOCK, id("hercules_beetle_charge_immune_blocks")); private Blocks() { } @@ -88,6 +89,7 @@ public final class AntarchyTags { public static final TagKey CATERPILLAR_FOODS = TagKey.create(Registries.ITEM, id("caterpillar_foods")); public static final TagKey BUTTERFLY_BREEDING_FOODS = TagKey.create(Registries.ITEM, id("butterfly_breeding_foods")); public static final TagKey REVERIE_DUPLICATION_ITEMS = TagKey.create(Registries.ITEM, id("reverie_duplication_items")); + public static final TagKey HERCULES_BEETLE_FOOD = TagKey.create(Registries.ITEM, id("hercules_beetle_food")); private Items() { } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/StinkyBehavior.java b/common/src/main/java/com/craisinlord/antarchy/content/StinkyBehavior.java index 5bdb11b1..93581e62 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/StinkyBehavior.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/StinkyBehavior.java @@ -104,6 +104,19 @@ public final class StinkyBehavior { applyNearbyNausea(entity, STINK_BUG_AURA_RADIUS, STINK_BUG_BURST_NAUSEA_DURATION_TICKS); } + public static void applyBurstStinkyEffect(LivingEntity source, double radius, int durationTicks) { + if (source.level().isClientSide()) { + return; + } + for (LivingEntity entity : source.level().getEntitiesOfClass( + LivingEntity.class, + source.getBoundingBox().inflate(radius), + e -> e.isAlive() && e != source + )) { + entity.addEffect(new MobEffectInstance(AntarchyObjects.STINKY_EFFECT.get(), durationTicks, 0, false, true, true)); + } + } + public static void applyNearbyNausea(LivingEntity source, double radius, int durationTicks) { if (source.level().isClientSide()) { return; diff --git a/common/src/main/java/com/craisinlord/antarchy/content/client/HerculesBeetleImpactShakeClientState.java b/common/src/main/java/com/craisinlord/antarchy/content/client/HerculesBeetleImpactShakeClientState.java new file mode 100644 index 00000000..3619e950 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/client/HerculesBeetleImpactShakeClientState.java @@ -0,0 +1,26 @@ +package com.craisinlord.antarchy.content.client; + +public final class HerculesBeetleImpactShakeClientState { + private static int shakeTicks; + + private HerculesBeetleImpactShakeClientState() { + } + + public static void trigger(int ticks) { + shakeTicks = Math.max(shakeTicks, Math.max(0, ticks)); + } + + public static void tick() { + if (shakeTicks > 0) { + shakeTicks--; + } + } + + public static int getTicks() { + return shakeTicks; + } + + public static void clear() { + shakeTicks = 0; + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/client/model/HerculesBeetleModel.java b/common/src/main/java/com/craisinlord/antarchy/content/client/model/HerculesBeetleModel.java new file mode 100644 index 00000000..82f53661 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/client/model/HerculesBeetleModel.java @@ -0,0 +1,28 @@ +package com.craisinlord.antarchy.content.client.model; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import net.minecraft.resources.ResourceLocation; +import software.bernie.geckolib.model.GeoModel; + +public class HerculesBeetleModel extends GeoModel { + private static final ResourceLocation TEXTURE = + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/hercules_beetle/hercules_beetle.png"); + private static final ResourceLocation TEXTURE_SADDLED = + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/hercules_beetle/hercules_beetle_saddle.png"); + + @Override + public ResourceLocation getModelResource(HerculesBeetleEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/hercules_beetle.geo.json"); + } + + @Override + public ResourceLocation getTextureResource(HerculesBeetleEntity animatable) { + return animatable.hasSaddle() ? TEXTURE_SADDLED : TEXTURE; + } + + @Override + public ResourceLocation getAnimationResource(HerculesBeetleEntity animatable) { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/hercules_beetle.animation.json"); + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/client/renderer/HerculesBeetleRenderer.java b/common/src/main/java/com/craisinlord/antarchy/content/client/renderer/HerculesBeetleRenderer.java new file mode 100644 index 00000000..47ef67c4 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/client/renderer/HerculesBeetleRenderer.java @@ -0,0 +1,12 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.content.client.model.HerculesBeetleModel; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import software.bernie.geckolib.renderer.GeoEntityRenderer; + +public class HerculesBeetleRenderer extends GeoEntityRenderer { + public HerculesBeetleRenderer(EntityRendererProvider.Context context) { + super(context, new HerculesBeetleModel()); + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/client/screen/DorrieScreen.java b/common/src/main/java/com/craisinlord/antarchy/content/client/screen/DorrieScreen.java deleted file mode 100644 index dd8d23d9..00000000 --- a/common/src/main/java/com/craisinlord/antarchy/content/client/screen/DorrieScreen.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.craisinlord.antarchy.content.client.screen; - -import com.craisinlord.antarchy.Antarchy; -import com.craisinlord.antarchy.content.menu.DorrieMenu; -import com.mojang.blaze3d.systems.RenderSystem; -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 class DorrieScreen extends AbstractContainerScreen { - // A 176 × 130 texture: saddle slot area at top, player inventory below. - // Art file: assets/antarchy/textures/gui/container/dorrie.png - private static final ResourceLocation TEXTURE = - ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/container/dorrie.png"); - - public static final int TEXTURE_WIDTH = 176; - public static final int TEXTURE_HEIGHT = 130; - - public DorrieScreen(DorrieMenu menu, Inventory playerInventory, Component title) { - super(menu, playerInventory, title); - this.imageWidth = TEXTURE_WIDTH; - this.imageHeight = TEXTURE_HEIGHT; - this.inventoryLabelY = this.imageHeight - 94; - } - - @Override - protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) { - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - guiGraphics.blit(TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight); - } - - @Override - public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - this.renderBackground(guiGraphics, mouseX, mouseY, partialTick); - super.render(guiGraphics, mouseX, mouseY, partialTick); - this.renderTooltip(guiGraphics, mouseX, mouseY); - } -} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java index 9467c973..821d7bd9 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/DorrieEntity.java @@ -1,13 +1,14 @@ package com.craisinlord.antarchy.content.entity; -import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.config.AntarchySettings; -import com.craisinlord.antarchy.content.item.CheepOnAStickItem; +import java.util.EnumSet; +import java.util.List; +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.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; @@ -31,8 +32,6 @@ import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; import net.minecraft.world.entity.ai.goal.RandomSwimmingGoal; import net.minecraft.world.entity.ai.goal.TryFindWaterGoal; -import java.util.EnumSet; -import java.util.List; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.navigation.WaterBoundPathNavigation; import net.minecraft.world.entity.animal.Animal; @@ -40,7 +39,6 @@ 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.resources.ResourceLocation; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import software.bernie.geckolib.animatable.GeoEntity; @@ -54,6 +52,7 @@ import software.bernie.geckolib.util.GeckoLibUtil; public class DorrieEntity extends Animal implements GeoEntity { private static final ResourceLocation CHEEP_ITEM_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "cheep"); + private static final EntityDataAccessor HAS_SADDLE = SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor TAMED = @@ -62,8 +61,8 @@ public class DorrieEntity extends Animal implements GeoEntity { SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.INT); private static final int MAX_CHARGE_TICKS = 40; - private static final float BASE_SPEED = 0.6F; - private static final float BOOSTED_SPEED = 1.3F; + private static final float WATER_RIDE_SPEED = 1.26F; + private static final float LAND_BEACH_SPEED = 0.04F; private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim"); @@ -73,16 +72,21 @@ public class DorrieEntity extends Animal implements GeoEntity { private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); + private static final int RIDES_TO_TAME = 4; + private static final int BUCK_DELAY_TICKS = 60; + private int chargeTicks = 0; private boolean isCharging = false; private boolean isLeaping = false; private boolean wasInAir = false; private int landAnimTicks = 0; - private boolean isPressingJump = false; + private int rideAttempts = 0; + private int buckTimer = 0; + private int swimForwardTicks = 0; public DorrieEntity(EntityType entityType, Level level) { super(entityType, level); - this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, BASE_SPEED, 1.0F, true); + this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 1.0F, true); this.lookControl = new SmoothSwimmingLookControl(this, 10); } @@ -113,12 +117,11 @@ public class DorrieEntity extends Animal implements GeoEntity { public static AttributeSupplier.Builder createAttributes() { return Mob.createMobAttributes() .add(Attributes.MAX_HEALTH, AntarchySettings.dorrieHealth()) - .add(Attributes.MOVEMENT_SPEED, BASE_SPEED) + .add(Attributes.MOVEMENT_SPEED, 1.0D) .add(Attributes.FOLLOW_RANGE, 16.0D) .add(Attributes.ATTACK_DAMAGE, 4.0D); } - public boolean hasSaddle() { return this.entityData.get(HAS_SADDLE); } @@ -143,12 +146,12 @@ public class DorrieEntity extends Animal implements GeoEntity { this.entityData.set(JUMP_CHARGE, charge); } - @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); tag.putBoolean("Tamed", this.isTamed()); tag.putBoolean("HasSaddle", this.hasSaddle()); + tag.putInt("RideAttempts", this.rideAttempts); } @Override @@ -156,28 +159,20 @@ public class DorrieEntity extends Animal implements GeoEntity { super.readAdditionalSaveData(tag); this.setTamed(tag.getBoolean("Tamed")); this.setSaddle(tag.getBoolean("HasSaddle")); + this.rideAttempts = tag.getInt("RideAttempts"); } - @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); if (!this.isTamed()) { - if (!this.level().isClientSide && this.isCheepItem(stack)) { - if (!player.getAbilities().instabuild) stack.shrink(1); - if (this.random.nextInt(3) == 0) { - this.setTamed(true); - this.playSound(SoundEvents.HORSE_BREATHE, 0.5F, 1.0F); - } else { - this.playSound(SoundEvents.DOLPHIN_HURT, 0.5F, 1.0F); - } - return InteractionResult.sidedSuccess(this.level().isClientSide); + if (!this.level().isClientSide) { + player.startRiding(this); } - return super.mobInteract(player, hand); + return InteractionResult.sidedSuccess(this.level().isClientSide); } - // Remove saddle with empty hand if (this.hasSaddle() && stack.isEmpty() && !player.isPassengerOfSameVehicle(this)) { if (!this.level().isClientSide) { this.setSaddle(false); @@ -187,99 +182,112 @@ public class DorrieEntity extends Animal implements GeoEntity { return InteractionResult.sidedSuccess(this.level().isClientSide); } - // Apply saddle if (!this.hasSaddle() && stack.is(Items.SADDLE)) { if (!this.level().isClientSide) { this.setSaddle(true); - if (!player.getAbilities().instabuild) stack.shrink(1); + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 1.0F); } return InteractionResult.sidedSuccess(this.level().isClientSide); } - // Mount if (this.hasSaddle() && !player.isPassengerOfSameVehicle(this)) { - if (!this.level().isClientSide) player.startRiding(this); + if (!this.level().isClientSide) { + player.startRiding(this); + } return InteractionResult.sidedSuccess(this.level().isClientSide); } return super.mobInteract(player, hand); } - @Override - public boolean isFood(ItemStack stack) { - return this.isCheepItem(stack); - } - - @Override @Nullable public LivingEntity getControllingPassenger() { - if (!this.isTamed() || !this.hasSaddle()) return null; + if (!this.isTamed() || !this.hasSaddle()) { + return null; + } Entity passenger = this.getFirstPassenger(); - if (passenger instanceof Player player) return player; + if (passenger instanceof Player player) { + return player; + } return null; } - private boolean riderHasCheepStick(Player player) { - return player.getMainHandItem().getItem() instanceof CheepOnAStickItem - || player.getOffhandItem().getItem() instanceof CheepOnAStickItem; - } - - private ItemStack getCheepStick(Player player) { - if (player.getMainHandItem().getItem() instanceof CheepOnAStickItem) return player.getMainHandItem(); - if (player.getOffhandItem().getItem() instanceof CheepOnAStickItem) return player.getOffhandItem(); - return ItemStack.EMPTY; - } - @Override protected Vec3 getRiddenInput(Player player, Vec3 travelVector) { - if (!riderHasCheepStick(player)) return Vec3.ZERO; - float forward = player.zza > 0 ? player.zza : player.zza / 4.0F; + float forward = player.zza; float strafe = player.xxa * 0.5F; - return new Vec3(strafe, travelVector.y, forward); + return new Vec3(strafe, 0, forward); } @Override protected float getRiddenSpeed(Player player) { - float speed = BASE_SPEED; - if (riderHasCheepStick(player)) { - speed = BOOSTED_SPEED; - if (!this.level().isClientSide) { - ItemStack stick = getCheepStick(player); - if (!stick.isEmpty() && this.tickCount % 10 == 0) { - stick.hurtAndBreak(1, player, - player.getMainHandItem().getItem() instanceof CheepOnAStickItem - ? net.minecraft.world.entity.EquipmentSlot.MAINHAND - : net.minecraft.world.entity.EquipmentSlot.OFFHAND); - } - } - } + float speed = (this.isInWater() || this.isInLava()) + ? WATER_RIDE_SPEED * (1.0F + Math.min(this.swimForwardTicks, 60) * 0.025F) + : LAND_BEACH_SPEED; return (float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * speed; } - @Override public void tick() { super.tick(); - if (!this.level().isClientSide) { - LivingEntity passenger = this.getControllingPassenger(); - - if (passenger != null && isPressingJump) { - Vec3 mov = this.getDeltaMovement(); - double boost = this.isInWater() ? 0.1D : 0.04D; - this.setDeltaMovement(mov.x, Math.min(mov.y + boost, 0.5D), mov.z); - this.hasImpulse = true; - } - - if (passenger instanceof Player && isCharging) { - chargeTicks = Math.min(chargeTicks + 1, MAX_CHARGE_TICKS); - setJumpCharge((int) ((chargeTicks / (float) MAX_CHARGE_TICKS) * 100)); + if (this.getControllingPassenger() instanceof Player player) { + this.setYRot(player.getYRot()); + this.yRotO = this.getYRot(); + this.yBodyRot = this.getYRot(); + this.yHeadRot = this.getYRot(); + if (this.isInWater() || this.isInLava()) { + if (player.zza > 0.05F) { + this.swimForwardTicks = Math.min(this.swimForwardTicks + 1, 60); + } else { + this.swimForwardTicks = Math.max(this.swimForwardTicks - 2, 0); + } + } else { + this.swimForwardTicks = 0; } + } else { + this.swimForwardTicks = 0; } - if (landAnimTicks > 0) landAnimTicks--; + if (!this.level().isClientSide && !this.isTamed() && this.getFirstPassenger() instanceof Player rider) { + buckTimer++; + if (buckTimer >= BUCK_DELAY_TICKS) { + rider.stopRiding(); + buckTimer = 0; + rideAttempts++; + if (rideAttempts >= RIDES_TO_TAME) { + this.setTamed(true); + this.playSound(SoundEvents.HORSE_BREATHE, 0.8F, 1.0F); + } else { + this.playSound(SoundEvents.HORSE_ANGRY, 0.8F, 1.0F); + } + } + } else if (!this.isTamed() && this.getFirstPassenger() == null) { + buckTimer = 0; + } + + if (!this.level().isClientSide && this.getControllingPassenger() instanceof Player && isCharging) { + chargeTicks = Math.min(chargeTicks + 1, MAX_CHARGE_TICKS); + setJumpCharge((int) ((chargeTicks / (float) MAX_CHARGE_TICKS) * 100)); + } + + // Surface buoyancy: when in water and not mid-leap, push toward the waterline. + // Stronger upward force when fully submerged, gentle when at the surface so + // Dorrie glides on top rather than diving or bouncing. + if (this.isInWater() && !isLeaping) { + Vec3 mov = this.getDeltaMovement(); + double push = this.isUnderWater() ? 0.07D : 0.015D; + double cappedY = this.isUnderWater() ? 0.3D : 0.05D; + this.setDeltaMovement(mov.x, Math.min(mov.y + push, cappedY), mov.z); + } + + if (landAnimTicks > 0) { + landAnimTicks--; + } boolean inAirNow = !this.onGround() && !this.isInWater() && !this.isInLava(); if (wasInAir && !inAirNow && isLeaping) { @@ -289,32 +297,29 @@ public class DorrieEntity extends Animal implements GeoEntity { wasInAir = inAirNow; } + public void setPressingJump(boolean pressing) {} - /** Called when the rider presses/releases space (up movement). */ - public void setPressingJump(boolean pressing) { - this.isPressingJump = pressing; - } - - /** Called when the rider presses left-control (charge start). */ public void startJumpCharge() { if (!this.level().isClientSide) { isCharging = true; } } - /** Called when the rider releases left-control (charge release). */ public void releaseJump() { if (!this.level().isClientSide && isCharging) { float power = chargeTicks / (float) MAX_CHARGE_TICKS; if (power > 0.05F) { - double horizontal = 0.8D + power * 1.4D; - double vertical = 0.4D + power * 0.8D; Vec3 look = this.getLookAngle(); Vec3 current = this.getDeltaMovement(); + // Add to existing momentum — no direction snap. The rider already has + // forward speed, so the jump just launches them upward and slightly + // accelerates in the direction they're already facing. + double forwardAdd = power * 0.5D; + double vertical = 0.55D + power * 0.9D; this.setDeltaMovement( - look.x * horizontal * 0.7D + current.x * 0.3D, + current.x + look.x * forwardAdd, vertical, - look.z * horizontal * 0.7D + current.z * 0.3D + current.z + look.z * forwardAdd ); this.hasImpulse = true; isLeaping = true; @@ -325,14 +330,14 @@ public class DorrieEntity extends Animal implements GeoEntity { } } - @Override protected void positionRider(Entity passenger, MoveFunction moveFunction) { if (this.hasPassenger(passenger)) { + Vec3 forward = this.getLookAngle().multiply(1.0D, 0.0D, 1.0D); moveFunction.accept(passenger, - this.getX(), - this.getY() + this.getPassengersRidingOffset() + passenger.getBbHeight() * 0.5D, - this.getZ()); + this.getX() - forward.x * 0.25D, + this.getY() + 0.5D, + this.getZ() - forward.z * 0.25D); } } @@ -340,7 +345,6 @@ public class DorrieEntity extends Animal implements GeoEntity { return 0.6D; } - @Override protected void dropEquipment() { super.dropEquipment(); @@ -349,14 +353,12 @@ public class DorrieEntity extends Animal implements GeoEntity { } } - @Override @Nullable public DorrieEntity getBreedOffspring(ServerLevel level, AgeableMob mob) { return null; } - @Override public boolean canBeLeashed() { return true; @@ -382,14 +384,15 @@ public class DorrieEntity extends Animal implements GeoEntity { return SoundEvents.DOLPHIN_SWIM; } - @Override public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { controllers.add(new AnimationController<>(this, "main_controller", 2, this::mainAnimController)); } private PlayState mainAnimController(AnimationState state) { - if (landAnimTicks > 0) return state.setAndContinue(JUMP_LAND_ANIM); + if (landAnimTicks > 0) { + return state.setAndContinue(JUMP_LAND_ANIM); + } if (isLeaping) { return state.setAndContinue(wasInAir ? JUMP_LOOP_ANIM : JUMP_START_ANIM); } @@ -401,12 +404,17 @@ public class DorrieEntity extends Animal implements GeoEntity { return this.geoCache; } + @Override + public boolean isFood(ItemStack stack) { + return this.isCheepItem(stack); + } + private boolean isCheepItem(ItemStack stack) { return !stack.isEmpty() && stack.is(BuiltInRegistries.ITEM.getOptional(CHEEP_ITEM_ID).orElse(Items.AIR)); } private static class HuntCheepGoal extends Goal { - private static final int COOLDOWN_TICKS = 600; // 30 seconds + private static final int COOLDOWN_TICKS = 600; private static final double HUNT_RANGE = 16.0D; private static final double ATTACK_RANGE_SQ = 2.5D * 2.5D; @@ -421,13 +429,21 @@ public class DorrieEntity extends Animal implements GeoEntity { @Override public boolean canUse() { - if (dorrie.level().isClientSide) return false; - if (cooldown > 0) { cooldown--; return false; } + if (dorrie.level().isClientSide) { + return false; + } + if (cooldown > 0) { + cooldown--; + return false; + } + if (dorrie.getControllingPassenger() != null) { + return false; + } List nearby = dorrie.level().getEntitiesOfClass( - CheepEntity.class, - dorrie.getBoundingBox().inflate(HUNT_RANGE) - ); - if (nearby.isEmpty()) return false; + CheepEntity.class, dorrie.getBoundingBox().inflate(HUNT_RANGE)); + if (nearby.isEmpty()) { + return false; + } nearby.sort((a, b) -> Double.compare(a.distanceToSqr(dorrie), b.distanceToSqr(dorrie))); target = nearby.get(0); return true; @@ -435,7 +451,9 @@ public class DorrieEntity extends Animal implements GeoEntity { @Override public boolean canContinueToUse() { - return target != null && target.isAlive() && dorrie.distanceToSqr(target) <= HUNT_RANGE * HUNT_RANGE * 4; + return target != null && target.isAlive() + && dorrie.getControllingPassenger() == null + && dorrie.distanceToSqr(target) <= HUNT_RANGE * HUNT_RANGE * 4; } @Override @@ -445,11 +463,14 @@ public class DorrieEntity extends Animal implements GeoEntity { @Override public void tick() { - if (target == null) return; + if (target == null) { + return; + } dorrie.getLookControl().setLookAt(target, 30.0F, 30.0F); dorrie.getNavigation().moveTo(target, 1.4D); if (dorrie.distanceToSqr(target) <= ATTACK_RANGE_SQ) { - target.hurt(dorrie.damageSources().mobAttack(dorrie), (float) dorrie.getAttributeValue(Attributes.ATTACK_DAMAGE)); + target.hurt(dorrie.damageSources().mobAttack(dorrie), + (float) dorrie.getAttributeValue(Attributes.ATTACK_DAMAGE)); stop(); } } @@ -460,6 +481,5 @@ public class DorrieEntity extends Animal implements GeoEntity { dorrie.getNavigation().stop(); cooldown = COOLDOWN_TICKS; } - } } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/HerculesBeetleEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/HerculesBeetleEntity.java new file mode 100644 index 00000000..4cc98fb1 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/HerculesBeetleEntity.java @@ -0,0 +1,1233 @@ +package com.craisinlord.antarchy.content.entity; + +import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.AntarchyTags; +import com.craisinlord.antarchy.content.item.BigBerthaItem; +import com.craisinlord.antarchy.content.network.HerculesBeetleImpactShakeSync; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +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.network.chat.Component; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerBossEvent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.tags.DamageTypeTags; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.BossEvent; +import net.minecraft.world.Difficulty; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.AgeableMob; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityDimensions; +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.PathfinderMob; +import net.minecraft.world.entity.TamableAnimal; +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.Goal; +import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; +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.animal.FlyingAnimal; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathType; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.entity.Pose; +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; + +public class HerculesBeetleEntity extends TamableAnimal implements GeoEntity, FlyingAnimal { + private static final EntityDataAccessor ANIMATION_STATE = + SynchedEntityData.defineId(HerculesBeetleEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor SADDLED = + SynchedEntityData.defineId(HerculesBeetleEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor FLYING = + SynchedEntityData.defineId(HerculesBeetleEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor KNOCKED_DOWN = + SynchedEntityData.defineId(HerculesBeetleEntity.class, EntityDataSerializers.BOOLEAN); + + private static final int ANIM_IDLE = 0; + private static final int ANIM_WALK = 1; + private static final int ANIM_FLY = 2; + private static final int ANIM_CRY = 3; + private static final int ANIM_ATTACK = 4; + private static final int ANIM_FLY_ATTACK = 5; + private static final int ANIM_CHARGE_START = 6; + private static final int ANIM_CHARGE = 7; + private static final int ANIM_KNOCKED_DOWN = 8; + private static final int ANIM_KNOCKED_DOWN_IDLE = 9; + private static final int ANIM_GET_UP = 10; + private static final int ANIM_OPEN_WINGS = 11; + private static final int ANIM_CLOSE_WINGS = 12; + private static final int ANIM_DEATH = 13; + + 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 CRY_ANIM = RawAnimation.begin().thenPlay("cry"); + private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack"); + private static final RawAnimation FLY_ATTACK_ANIM = RawAnimation.begin().thenPlay("fly_attack"); + private static final RawAnimation CHARGE_START_ANIM = RawAnimation.begin().thenPlay("charge_start"); + private static final RawAnimation CHARGE_ANIM = RawAnimation.begin().thenLoop("charge"); + private static final RawAnimation KNOCKED_DOWN_ANIM = RawAnimation.begin().thenPlay("knocked_down"); + private static final RawAnimation KNOCKED_DOWN_IDLE_ANIM = RawAnimation.begin().thenLoop("knocked_down_idle"); + private static final RawAnimation GET_UP_ANIM = RawAnimation.begin().thenPlay("get_up"); + private static final RawAnimation OPEN_WINGS_ANIM = RawAnimation.begin().thenPlay("open_wings"); + private static final RawAnimation CLOSE_WINGS_ANIM = RawAnimation.begin().thenPlay("close_wings"); + private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlayAndHold("death"); + + private static final int CRY_TICKS = 30; + private static final int ATTACK_TICKS = 10; + private static final int ATTACK_HIT_TICK = 5; + private static final int FLY_ATTACK_TICKS = 10; + private static final int FLY_ATTACK_HIT_TICK = 5; + private static final int OPEN_WINGS_TICKS = 10; + private static final int CLOSE_WINGS_TICKS = 5; + private static final int CHARGE_START_TICKS = 15; + private static final int KNOCKED_DOWN_TICKS = 16; + private static final int GET_UP_TICKS = 20; + private static final int MAX_PLAYER_CHARGE_TICKS = 40; + private static final int MIN_MOUNTED_CHARGE_TICKS = 40; + private static final int MAX_CHARGE_TICKS = 200; + private static final int CHARGE_COOLDOWN_TICKS = 200; + private static final int REGULAR_ATTACK_COOLDOWN_TICKS = 18; + private static final int CRY_COOLDOWN_TICKS = 120; + private static final int COMBAT_RESET_TICKS = 60; + private static final int IMPACT_SHAKE_TICKS = 25; + private static final double FLIGHT_SPEED = 1.38D; + private static final double GROUND_SPEED = 0.34D; + private static final double CHARGE_SPEED = 1.5D; + private static final double CHARGE_DISTANCE = 10.0D; + private static final double ATTACK_RANGE = 4.5D; + private static final double FLY_ATTACK_RANGE = 5.5D; + private static final double CHARGE_TRIGGER_RANGE = 12.0D; + private static final int MAX_BROKEN_BLOCKS = 32; + private static final EntityDimensions DEFAULT_DIMENSIONS = EntityDimensions.scalable(3.0F, 4.0F); + private static final double MOUNTED_FLYING_SPEED_MULTIPLIER = 0.14D; + + private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); + private final ServerBossEvent bossEvent = + new ServerBossEvent(Component.translatable("entity.antarchy.hercules_beetle"), BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.PROGRESS); + + private int actionTicks; + private int actionHitTick; + private int chargeTicksRemaining; + private double chargeDistanceTravelled; + private double chargeDistanceTarget; + private int chargeCooldown; + private int attackCooldown; + private int cryCooldown; + private int flightCooldown; + private int targetlessTicks; + private int groundedTicks; + private boolean attackDamageApplied; + private boolean riderJumpPressed; + private boolean combatCryDone; + private boolean pendingChargeStart; + private boolean pendingMountedCharge; + private int pendingChargeDuration; + @Nullable + private UUID attackTargetId; + private Vec3 chargeDirection = Vec3.ZERO; + + public HerculesBeetleEntity(EntityType entityType, Level level) { + super(entityType, level); + this.moveControl = new FlyingMoveControl(this, 20, true); + this.setPathfindingMalus(PathType.WATER, -1.0F); + this.setPathfindingMalus(PathType.LAVA, -1.0F); + this.xpReward = 50; + } + + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes() + .add(Attributes.MAX_HEALTH, AntarchySettings.herculesBeetleHealth()) + .add(Attributes.ATTACK_DAMAGE, AntarchySettings.herculesBeetleAttackDamage()) + .add(Attributes.MOVEMENT_SPEED, GROUND_SPEED) + .add(Attributes.FLYING_SPEED, FLIGHT_SPEED) + .add(Attributes.FOLLOW_RANGE, 48.0D) + .add(Attributes.KNOCKBACK_RESISTANCE, 0.9D) + .add(Attributes.ARMOR, 12.0D); + } + + public static boolean canSpawn(EntityType 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 + && level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP) + && level.isEmptyBlock(pos) + && level.isEmptyBlock(pos.above()); + } + + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation navigation = new FlyingPathNavigation(this, level); + navigation.setCanFloat(true); + navigation.setCanOpenDoors(false); + return navigation; + } + + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); + this.goalSelector.addGoal(1, new HerculesCombatGoal()); + this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.75D) { + @Override + public boolean canUse() { + return !HerculesBeetleEntity.this.isKnockedDown() + && !HerculesBeetleEntity.this.isVehicle() + && !HerculesBeetleEntity.this.isInBusyAnimation() + && super.canUse(); + } + }); + this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 16.0F)); + this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); + this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, entity -> + entity instanceof Player player + && !this.isTame() + && !this.isKnockedDown() + && !player.isCreative() + && !player.isSpectator())); + } + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); + builder.define(ANIMATION_STATE, ANIM_IDLE); + builder.define(SADDLED, false); + builder.define(FLYING, false); + builder.define(KNOCKED_DOWN, false); + } + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + controllers.add(new AnimationController<>(this, "main_controller", 0, this::mainAnimController)); + } + + private PlayState mainAnimController(AnimationState state) { + return switch (this.getAnimationState()) { + case ANIM_WALK -> state.setAndContinue(WALK_ANIM); + case ANIM_FLY -> state.setAndContinue(FLY_ANIM); + case ANIM_CRY -> state.setAndContinue(CRY_ANIM); + case ANIM_ATTACK -> state.setAndContinue(ATTACK_ANIM); + case ANIM_FLY_ATTACK -> state.setAndContinue(FLY_ATTACK_ANIM); + case ANIM_CHARGE_START -> state.setAndContinue(CHARGE_START_ANIM); + case ANIM_CHARGE -> state.setAndContinue(CHARGE_ANIM); + case ANIM_KNOCKED_DOWN -> state.setAndContinue(KNOCKED_DOWN_ANIM); + case ANIM_KNOCKED_DOWN_IDLE -> state.setAndContinue(KNOCKED_DOWN_IDLE_ANIM); + case ANIM_GET_UP -> state.setAndContinue(GET_UP_ANIM); + case ANIM_OPEN_WINGS -> state.setAndContinue(OPEN_WINGS_ANIM); + case ANIM_CLOSE_WINGS -> state.setAndContinue(CLOSE_WINGS_ANIM); + case ANIM_DEATH -> state.setAndContinue(DEATH_ANIM); + default -> state.setAndContinue(IDLE_ANIM); + }; + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return this.geoCache; + } + + @Override + public void tick() { + super.tick(); + + if (this.level().isClientSide) { + return; + } + + this.syncConfiguredAttributes(); + + if (this.attackCooldown > 0) { + this.attackCooldown--; + } + if (this.chargeCooldown > 0) { + this.chargeCooldown--; + } + if (this.cryCooldown > 0) { + this.cryCooldown--; + } + + if (this.getTarget() == null) { + if (this.targetlessTicks++ >= COMBAT_RESET_TICKS) { + this.combatCryDone = false; + } + } else { + this.targetlessTicks = 0; + } + + if (this.onGround()) { + this.groundedTicks++; + } else { + this.groundedTicks = 0; + } + + if (this.isDeadOrDying()) { + this.noPhysics = false; + this.setAnimationState(ANIM_DEATH); + this.bossEvent.setProgress(0.0F); + return; + } + + if (this.flightCooldown > 0) { + this.flightCooldown--; + } + + if (this.isKnockedDown()) { + this.tickKnockedDown(); + } else if (this.chargeTicksRemaining > 0 || this.getAnimationState() == ANIM_CHARGE) { + this.tickCharge(); + } else if (this.isInBusyAnimation()) { + this.tickBusyAnimation(); + } else { + this.tickFlightState(); + this.updateAmbientAnimation(); + } + + if (this.isVehicle()) { + this.tickMountedMotion(); + } + + this.setNoGravity(this.isFlying() || this.chargeTicksRemaining > 0); + if (this.isFlying() || this.chargeTicksRemaining > 0) { + this.fallDistance = 0.0F; + } + + if (this.shouldShowBossBar()) { + this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); + } else { + this.bossEvent.removeAllPlayers(); + } + } + + private void tickKnockedDown() { + this.getNavigation().stop(); + this.setDeltaMovement(Vec3.ZERO); + if (this.getAnimationState() == ANIM_KNOCKED_DOWN && this.actionTicks > 0) { + this.actionTicks--; + if (this.actionTicks <= 0) { + this.setAnimationState(ANIM_KNOCKED_DOWN_IDLE); + } + } else if (this.getAnimationState() != ANIM_KNOCKED_DOWN_IDLE) { + this.setAnimationState(ANIM_KNOCKED_DOWN_IDLE); + } + } + + private void tickBusyAnimation() { + this.getNavigation().stop(); + if (this.getTarget() != null) { + this.getLookControl().setLookAt(this.getTarget(), 25.0F, 20.0F); + } + + int state = this.getAnimationState(); + if (state == ANIM_ATTACK || state == ANIM_FLY_ATTACK) { + int elapsed = (state == ANIM_ATTACK ? ATTACK_TICKS : FLY_ATTACK_TICKS) - this.actionTicks; + if (!this.attackDamageApplied && elapsed >= this.actionHitTick) { + this.attackDamageApplied = true; + this.performRegularAttack(state == ANIM_FLY_ATTACK); + } + } + + if (this.actionTicks > 0) { + this.actionTicks--; + } + if (this.actionTicks > 0) { + return; + } + + switch (state) { + case ANIM_OPEN_WINGS -> { + this.setFlying(true); + if (this.pendingChargeStart) { + this.pendingChargeStart = false; + this.beginChargeStart(this.chargeDirection, this.pendingChargeDuration, this.pendingMountedCharge); + return; + } + } + case ANIM_CLOSE_WINGS -> this.setFlying(false); + case ANIM_CHARGE_START -> this.beginChargeLoop(); + case ANIM_GET_UP -> this.setAnimationState(this.isFlying() ? ANIM_FLY : ANIM_IDLE); + case ANIM_CRY, ANIM_ATTACK, ANIM_FLY_ATTACK -> { + } + default -> { + } + } + + this.updateAmbientAnimation(); + } + + private void tickCharge() { + if (this.getAnimationState() != ANIM_CHARGE) { + this.setAnimationState(ANIM_CHARGE); + } + this.setFlying(true); + this.setNoGravity(true); + this.noPhysics = true; + this.fallDistance = 0.0F; + + double remainingDistance = Math.max(0.0D, this.chargeDistanceTarget - this.chargeDistanceTravelled); + if (remainingDistance <= 0.0D) { + this.finishChargeImpact(); + return; + } + + Vec3 move = this.chargeDirection.scale(Math.min(CHARGE_SPEED, remainingDistance)); + Vec3 nextPosition = this.position().add(move); + if (this.blocksChargePath(nextPosition)) { + this.finishChargeImpact(); + return; + } + + this.breakBlocksForChargePath(nextPosition); + this.setDeltaMovement(move); + this.move(MoverType.SELF, this.getDeltaMovement()); + this.lookInDirection(this.chargeDirection); + + this.chargeDistanceTravelled += move.length(); + this.chargeTicksRemaining--; + if (this.chargeDistanceTravelled >= this.chargeDistanceTarget || this.chargeTicksRemaining <= 0) { + this.finishChargeImpact(); + } + } + + private void tickFlightState() { + if (this.isFlying() && this.onGround() && this.groundedTicks > 10 && this.getTarget() == null && !this.riderJumpPressed) { + this.requestFlightStop(); + } + } + + private void tickMountedMotion() { + if (!(this.getControllingPassenger() instanceof Player rider) || this.isKnockedDown()) { + return; + } + if (this.chargeTicksRemaining > 0 || this.getAnimationState() == ANIM_CHARGE_START || this.getAnimationState() == ANIM_OPEN_WINGS) { + return; + } + + this.setYRot(rider.getYHeadRot()); + this.yHeadRot = this.getYRot(); + this.yBodyRot = this.getYRot(); + + if (this.onGround() && this.isFlying()) { + this.requestFlightStop(); + return; + } + + boolean wantsFlight = this.riderJumpPressed || rider.getXRot() < -5.0F || this.fallDistance > 2.5F; + if (wantsFlight && !this.isFlying() && this.onGround() && this.flightCooldown <= 0) { + this.requestFlightStart(); + } + } + + private void updateAmbientAnimation() { + if (this.isKnockedDown()) { + this.setAnimationState(ANIM_KNOCKED_DOWN_IDLE); + return; + } + if (this.isFlying()) { + this.setAnimationState(ANIM_FLY); + return; + } + if (this.getControllingPassenger() instanceof Player rider + && (Math.abs(rider.zza) > 0.05F || Math.abs(rider.xxa) > 0.05F)) { + this.setAnimationState(ANIM_WALK); + return; + } + Vec3 motion = this.getDeltaMovement(); + boolean moving = motion.horizontalDistanceSqr() > 0.0025D; + this.setAnimationState(moving ? ANIM_WALK : ANIM_IDLE); + } + + @Override + protected Vec3 getRiddenInput(Player player, Vec3 travelVector) { + if (!this.isFlying()) { + return new Vec3(player.xxa * 0.35D, 0.0D, player.zza); + } + + double vertical = 0.0D; + if (this.riderJumpPressed) { + vertical += 0.18D; + } + if (player.zza > 0.0F && player.getXRot() < -5.0F) { + vertical += 0.16D; + } else if (player.zza > 0.0F && player.getXRot() > 20.0F) { + vertical -= 0.16D; + } + + return new Vec3(player.xxa * 0.35D, vertical, player.zza); + } + + @Override + protected float getRiddenSpeed(Player player) { + if (this.isFlying()) { + return (float) (this.getAttributeValue(Attributes.FLYING_SPEED) * MOUNTED_FLYING_SPEED_MULTIPLIER); + } + return (float) this.getAttributeValue(Attributes.MOVEMENT_SPEED); + } + + private void syncConfiguredAttributes() { + Objects.requireNonNull(this.getAttribute(Attributes.MAX_HEALTH)).setBaseValue(AntarchySettings.herculesBeetleHealth()); + Objects.requireNonNull(this.getAttribute(Attributes.ATTACK_DAMAGE)).setBaseValue(AntarchySettings.herculesBeetleAttackDamage()); + if (this.getHealth() > this.getMaxHealth()) { + this.setHealth(this.getMaxHealth()); + } + } + + @Override + public boolean hurt(DamageSource source, float amount) { + if (source.is(DamageTypeTags.IS_FALL)) { + return false; + } + + if (source.is(DamageTypeTags.IS_PROJECTILE)) { + amount *= 0.5F; + } + + if (!this.level().isClientSide && !this.isTame() && !this.isKnockedDown() && !this.isDeadOrDying()) { + float knockThreshold = Math.max(1.0F, this.getMaxHealth() * 0.05F); + if (this.getHealth() - amount <= knockThreshold) { + float allowedDamage = Math.max(0.0F, this.getHealth() - knockThreshold); + boolean hurt = allowedDamage <= 0.0F || super.hurt(source, allowedDamage); + if (hurt && this.isAlive()) { + this.enterKnockedDown(); + } + return hurt; + } + } + + return super.hurt(source, amount); + } + + private void enterKnockedDown() { + if (this.isKnockedDown()) { + return; + } + this.setKnockedDown(true); + this.setFlying(false); + this.setNoGravity(false); + this.noPhysics = false; + this.pendingChargeStart = false; + this.chargeTicksRemaining = 0; + this.attackCooldown = 0; + this.getNavigation().stop(); + this.setTarget(null); + this.setDeltaMovement(Vec3.ZERO); + this.actionTicks = KNOCKED_DOWN_TICKS; + this.setAnimationState(ANIM_KNOCKED_DOWN); + } + + @Override + public InteractionResult mobInteract(Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + + if (this.isKnockedDown() && this.isFood(stack)) { + if (!this.level().isClientSide) { + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } + if (this.random.nextInt(3) == 0) { + this.setTame(true, true); + this.setOwnerUUID(player.getUUID()); + this.setKnockedDown(false); + this.heal(this.getMaxHealth()); + this.setTarget(null); + this.actionTicks = GET_UP_TICKS; + this.setAnimationState(ANIM_GET_UP); + this.level().broadcastEntityEvent(this, (byte) 7); + this.bossEvent.removeAllPlayers(); + } else { + this.level().broadcastEntityEvent(this, (byte) 6); + } + } + return InteractionResult.sidedSuccess(this.level().isClientSide); + } + + if (this.isTame()) { + if (!this.hasSaddle() && stack.is(Items.SADDLE) && this.isOwnedBy(player)) { + if (!this.level().isClientSide) { + this.setSaddled(true); + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } + this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 1.0F); + } + return InteractionResult.sidedSuccess(this.level().isClientSide); + } + + if (this.hasSaddle() && stack.isEmpty() && player.isShiftKeyDown() && this.isOwnedBy(player) && !player.isPassengerOfSameVehicle(this)) { + if (!this.level().isClientSide) { + this.setSaddled(false); + this.spawnAtLocation(new ItemStack(Items.SADDLE)); + this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 0.8F); + } + return InteractionResult.sidedSuccess(this.level().isClientSide); + } + + if (this.hasSaddle() && stack.isEmpty() && !player.isPassengerOfSameVehicle(this) && !this.isKnockedDown()) { + if (!this.level().isClientSide) { + player.startRiding(this); + } + return InteractionResult.sidedSuccess(this.level().isClientSide); + } + } + + return super.mobInteract(player, hand); + } + + @Override + public boolean isFood(ItemStack stack) { + return stack.is(AntarchyTags.Items.HERCULES_BEETLE_FOOD); + } + + public boolean hasSaddle() { + return this.entityData.get(SADDLED); + } + + public void setSaddled(boolean saddled) { + this.entityData.set(SADDLED, saddled); + } + + private void setFlying(boolean flying) { + if (this.entityData.get(FLYING) != flying) { + this.entityData.set(FLYING, flying); + this.refreshDimensions(); + } + } + + public boolean isKnockedDown() { + return this.entityData.get(KNOCKED_DOWN); + } + + private void setKnockedDown(boolean knockedDown) { + this.entityData.set(KNOCKED_DOWN, knockedDown); + } + + public int getAnimationState() { + return this.entityData.get(ANIMATION_STATE); + } + + private void setAnimationState(int animationState) { + this.entityData.set(ANIMATION_STATE, animationState); + } + + private boolean isInBusyAnimation() { + int state = this.getAnimationState(); + return state == ANIM_CRY + || state == ANIM_ATTACK + || state == ANIM_FLY_ATTACK + || state == ANIM_CHARGE_START + || state == ANIM_CHARGE + || state == ANIM_OPEN_WINGS + || state == ANIM_CLOSE_WINGS + || state == ANIM_GET_UP + || state == ANIM_KNOCKED_DOWN; + } + + private void requestFlightStart() { + if (this.isFlying() + || this.isKnockedDown() + || this.getAnimationState() == ANIM_OPEN_WINGS + || this.getAnimationState() == ANIM_CHARGE + || this.getAnimationState() == ANIM_CHARGE_START + || !this.onGround() + || this.flightCooldown > 0) { + return; + } + this.setAnimationState(ANIM_OPEN_WINGS); + this.actionTicks = OPEN_WINGS_TICKS; + } + + private void requestFlightStop() { + if (!this.isFlying() || this.getAnimationState() == ANIM_CLOSE_WINGS || this.isInBusyAnimation()) { + return; + } + this.setAnimationState(ANIM_CLOSE_WINGS); + this.actionTicks = CLOSE_WINGS_TICKS; + this.flightCooldown = 8; + } + + private void startCry() { + this.setAnimationState(ANIM_CRY); + this.actionTicks = CRY_TICKS; + this.cryCooldown = CRY_COOLDOWN_TICKS; + this.combatCryDone = true; + this.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, 200, 1)); + this.playSound(SoundEvents.RAVAGER_ROAR, 1.5F, 0.7F); + } + + private void startRegularAttack(boolean flyingAttack, @Nullable LivingEntity target) { + this.setAnimationState(flyingAttack ? ANIM_FLY_ATTACK : ANIM_ATTACK); + this.actionTicks = flyingAttack ? FLY_ATTACK_TICKS : ATTACK_TICKS; + this.actionHitTick = flyingAttack ? FLY_ATTACK_HIT_TICK : ATTACK_HIT_TICK; + this.attackDamageApplied = false; + this.attackTargetId = target == null ? null : target.getUUID(); + this.attackCooldown = REGULAR_ATTACK_COOLDOWN_TICKS; + } + + private void performRegularAttack(boolean flyingAttack) { + LivingEntity target = this.attackTargetId == null ? null : this.findNearbyLivingEntity(this.attackTargetId); + boolean hit = false; + if (target != null && target.isAlive() && this.distanceToSqr(target) <= 49.0D) { + hit = this.hurtAndLaunchTarget(target, (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE), flyingAttack ? 1.0D : 0.7D); + } + if (!hit) { + for (LivingEntity candidate : this.getFrontTargets(flyingAttack ? 6.0D : 5.0D, 2.5D)) { + if (this.hurtAndLaunchTarget(candidate, (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE), flyingAttack ? 1.0D : 0.7D)) { + break; + } + } + } + this.playSound(SoundEvents.RAVAGER_ATTACK, 1.2F, flyingAttack ? 1.1F : 0.9F); + } + + private boolean hurtAndLaunchTarget(LivingEntity target, float damage, double horizontalKnockback) { + if (!this.canHarm(target)) { + return false; + } + boolean hurt = target.hurt(this.damageSources().mobAttack(this), damage); + if (hurt) { + Vec3 push = target.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D); + if (push.lengthSqr() < 1.0E-4D) { + push = this.getLookAngle().multiply(1.0D, 0.0D, 1.0D); + } + push = push.normalize().scale(horizontalKnockback); + target.setDeltaMovement(target.getDeltaMovement().add(push.x, 1.25D, push.z)); + target.hurtMarked = true; + } + return hurt; + } + + private void startCharge(Vec3 direction, int durationTicks, boolean mountedCharge) { + Vec3 horizontal = direction.multiply(1.0D, 0.0D, 1.0D); + if (horizontal.lengthSqr() < 1.0E-4D) { + horizontal = this.getLookAngle().multiply(1.0D, 0.0D, 1.0D); + } + this.chargeDirection = horizontal.normalize(); + this.pendingChargeDuration = Mth.clamp(durationTicks, MIN_MOUNTED_CHARGE_TICKS, MAX_CHARGE_TICKS); + this.pendingMountedCharge = mountedCharge; + + if (!this.isFlying()) { + this.pendingChargeStart = true; + this.requestFlightStart(); + return; + } + this.beginChargeStart(this.chargeDirection, this.pendingChargeDuration, mountedCharge); + } + + private void beginChargeStart(Vec3 direction, int durationTicks, boolean mountedCharge) { + this.chargeDirection = direction.normalize(); + this.pendingChargeDuration = durationTicks; + this.pendingMountedCharge = mountedCharge; + this.setAnimationState(ANIM_CHARGE_START); + this.actionTicks = CHARGE_START_TICKS; + this.playSound(SoundEvents.ENDER_DRAGON_FLAP, 1.3F, 0.7F); + } + + private void beginChargeLoop() { + this.setAnimationState(ANIM_CHARGE); + this.chargeTicksRemaining = this.pendingChargeDuration; + this.chargeDistanceTravelled = 0.0D; + this.chargeDistanceTarget = CHARGE_DISTANCE; + this.chargeCooldown = CHARGE_COOLDOWN_TICKS; + this.pendingChargeDuration = 0; + this.pendingMountedCharge = false; + this.setFlying(true); + } + + private void finishChargeImpact() { + if (!(this.level() instanceof ServerLevel serverLevel)) { + this.chargeTicksRemaining = 0; + this.noPhysics = false; + this.updateAmbientAnimation(); + return; + } + + this.chargeTicksRemaining = 0; + this.noPhysics = false; + this.setDeltaMovement(Vec3.ZERO); + + AABB impactBox = this.getBoundingBox().inflate(3.0D, 2.0D, 3.0D); + float damage = (float) AntarchySettings.herculesBeetleChargeDamage(); + for (LivingEntity victim : this.level().getEntitiesOfClass(LivingEntity.class, impactBox, this::canHarm)) { + if (victim == this) { + continue; + } + if (victim.hurt(this.damageSources().mobAttack(this), damage)) { + Vec3 push = victim.position().subtract(this.position()); + if (push.lengthSqr() < 1.0E-4D) { + push = this.chargeDirection; + } + push = push.normalize(); + victim.setDeltaMovement(victim.getDeltaMovement().add(push.x * 1.25D, 0.9D, push.z * 1.25D)); + victim.hurtMarked = true; + } + } + + this.breakBlocksForChargeImpact(serverLevel); + this.playSound(SoundEvents.GENERIC_EXPLODE.value(), 1.6F, 0.75F); + serverLevel.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY() + 0.8D, this.getZ(), 4, 1.5D, 0.5D, 1.5D, 0.0D); + serverLevel.sendParticles(ParticleTypes.CRIT, this.getX(), this.getY() + 0.8D, this.getZ(), 20, 2.0D, 0.6D, 2.0D, 0.08D); + this.broadcastImpactShake(serverLevel); + + if (this.onGround()) { + this.requestFlightStop(); + } else { + this.updateAmbientAnimation(); + } + } + + private void breakBlocksForChargeImpact(ServerLevel level) { + if (!level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { + return; + } + + AABB impactBox = this.getBoundingBox().inflate(3.0D, 2.0D, 3.0D); + int broken = 0; + for (BlockPos pos : BlockPos.betweenClosed( + BlockPos.containing(impactBox.minX, impactBox.minY, impactBox.minZ), + BlockPos.containing(impactBox.maxX, impactBox.maxY, impactBox.maxZ) + )) { + if (broken >= MAX_BROKEN_BLOCKS) { + break; + } + BlockState state = level.getBlockState(pos); + BlockEntity blockEntity = level.getBlockEntity(pos); + if (state.isAir() + || state.is(AntarchyTags.Blocks.HERCULES_BEETLE_CHARGE_IMMUNE_BLOCKS) + || blockEntity != null + || state.getDestroySpeed(level, pos) < 0.0F) { + continue; + } + if (level.destroyBlock(pos, true, this)) { + broken++; + } + } + } + + private void breakBlocksForChargePath(Vec3 nextPosition) { + if (!(this.level() instanceof ServerLevel serverLevel) || !serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { + return; + } + + AABB pathBox = this.getBoundingBox().expandTowards(nextPosition.subtract(this.position())).inflate(1.25D, 1.25D, 1.25D); + int broken = 0; + for (BlockPos pos : BlockPos.betweenClosed( + BlockPos.containing(pathBox.minX, pathBox.minY, pathBox.minZ), + BlockPos.containing(pathBox.maxX, pathBox.maxY, pathBox.maxZ) + )) { + if (broken >= MAX_BROKEN_BLOCKS) { + break; + } + BlockState state = serverLevel.getBlockState(pos); + BlockEntity blockEntity = serverLevel.getBlockEntity(pos); + if (state.isAir() + || state.is(AntarchyTags.Blocks.HERCULES_BEETLE_CHARGE_IMMUNE_BLOCKS) + || blockEntity != null + || state.getDestroySpeed(serverLevel, pos) < 0.0F) { + continue; + } + if (serverLevel.destroyBlock(pos, true, this)) { + broken++; + } + } + } + + private boolean blocksChargePath(Vec3 nextPosition) { + if (!(this.level() instanceof ServerLevel serverLevel)) { + return false; + } + + AABB pathBox = this.getBoundingBox().expandTowards(nextPosition.subtract(this.position())).inflate(1.25D, 1.25D, 1.25D); + for (BlockPos pos : BlockPos.betweenClosed( + BlockPos.containing(pathBox.minX, pathBox.minY, pathBox.minZ), + BlockPos.containing(pathBox.maxX, pathBox.maxY, pathBox.maxZ) + )) { + BlockState state = serverLevel.getBlockState(pos); + if (state.isAir()) { + continue; + } + if (state.is(AntarchyTags.Blocks.HERCULES_BEETLE_CHARGE_IMMUNE_BLOCKS) || serverLevel.getBlockEntity(pos) != null || state.getDestroySpeed(serverLevel, pos) < 0.0F) { + return true; + } + } + return false; + } + + private void broadcastImpactShake(ServerLevel level) { + for (ServerPlayer player : level.players()) { + if (player.distanceToSqr(this) <= 48.0D * 48.0D) { + HerculesBeetleImpactShakeSync.send(player, IMPACT_SHAKE_TICKS); + } + } + } + + public void setRiderJumpPressed(boolean riderJumpPressed) { + this.riderJumpPressed = riderJumpPressed; + } + + public void handleMountedRegularAttack(ServerPlayer player) { + if (!this.canPlayerControl(player) || this.attackCooldown > 0 || this.isKnockedDown() || this.isInBusyAnimation() || this.chargeTicksRemaining > 0) { + return; + } + LivingEntity target = this.findFrontTarget(6.0D, 3.0D); + this.startRegularAttack(this.isFlying(), target); + } + + public void handleMountedCharge(ServerPlayer player, int chargeTicks) { + if (!this.canPlayerControl(player) || this.chargeCooldown > 0 || this.isKnockedDown() || this.isInBusyAnimation() || this.chargeTicksRemaining > 0) { + return; + } + int clamped = Mth.clamp(chargeTicks, 0, MAX_PLAYER_CHARGE_TICKS); + if (clamped <= 0) { + return; + } + int duration = MIN_MOUNTED_CHARGE_TICKS + Mth.floor((MAX_CHARGE_TICKS - MIN_MOUNTED_CHARGE_TICKS) * (clamped / (float) MAX_PLAYER_CHARGE_TICKS)); + this.startCharge(player.getLookAngle(), duration, true); + } + + private boolean canPlayerControl(ServerPlayer player) { + return this.isTame() + && this.hasSaddle() + && player.getVehicle() == this + && this.getControllingPassenger() == player; + } + + @Override + @Nullable + public LivingEntity getControllingPassenger() { + if (!this.isTame() || !this.hasSaddle()) { + return null; + } + Entity passenger = this.getFirstPassenger(); + return passenger instanceof LivingEntity living ? living : null; + } + + @Override + protected void positionRider(Entity passenger, MoveFunction moveFunction) { + if (this.hasPassenger(passenger)) { + Vec3 forward = this.getLookAngle().multiply(1.0D, 0.0D, 1.0D); + double yOffset = this.isFlying() ? 5.3D : 0.5D; + double forwardOffset = this.isFlying() ? 2.2D : 0.0D; + moveFunction.accept( + passenger, + this.getX() + forward.x * forwardOffset, + this.getY() + yOffset, + this.getZ() + forward.z * forwardOffset + ); + } + } + + @Override + public boolean canAddPassenger(Entity passenger) { + return this.getPassengers().isEmpty(); + } + + @Override + public void travel(Vec3 travelVector) { + if (!(this.getControllingPassenger() instanceof Player rider) || !this.isVehicle()) { + super.travel(travelVector); + return; + } + + this.setYRot(rider.getYHeadRot()); + this.yBodyRot = this.getYRot(); + this.yHeadRot = this.getYRot(); + + Vec3 riddenInput = this.getRiddenInput(rider, travelVector); + this.setSpeed(this.getRiddenSpeed(rider)); + Vec3 motion = new Vec3(0.0D, this.getDeltaMovement().y, 0.0D); + this.moveRelative(this.getSpeed(), new Vec3(riddenInput.x, 0.0D, riddenInput.z)); + motion = motion.add(this.getDeltaMovement().x, riddenInput.y, this.getDeltaMovement().z); + this.setDeltaMovement(motion); + this.move(MoverType.SELF, this.getDeltaMovement()); + + if (!this.isFlying() && rider.zza > 0.0F && this.horizontalCollision) { + Vec3 stepUpPos = this.position().add(0.0D, 1.0D, 0.0D); + this.setPos(stepUpPos.x, stepUpPos.y, stepUpPos.z); + this.setDeltaMovement(Vec3.ZERO); + this.moveRelative(this.getSpeed(), new Vec3(riddenInput.x, 0.0D, riddenInput.z)); + this.move(MoverType.SELF, this.getDeltaMovement()); + } + } + + @Override + public boolean doHurtTarget(Entity target) { + if (this.attackCooldown > 0 || this.isKnockedDown()) { + return false; + } + if (target instanceof LivingEntity livingTarget) { + this.startRegularAttack(this.isFlying(), livingTarget); + return true; + } + return super.doHurtTarget(target); + } + + @Override + public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) { + return false; + } + + @Override + protected void checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos) { + } + + @Override + public boolean isFlying() { + return this.entityData.get(FLYING); + } + + @Override + public EntityDimensions getDefaultDimensions(Pose pose) { + return DEFAULT_DIMENSIONS; + } + + @Override + public boolean removeWhenFarAway(double distanceToClosestPlayer) { + return false; + } + + @Override + public boolean canAttack(LivingEntity target) { + if (this.isTame() && target instanceof Player) { + return false; + } + return super.canAttack(target); + } + + private boolean canHarm(@Nullable LivingEntity target) { + if (target == null || !target.isAlive() || target == this || target == this.getControllingPassenger()) { + return false; + } + if (target instanceof Player player) { + return !player.isCreative() && !player.isSpectator() && !this.isTame(); + } + return target.canBeSeenAsEnemy(); + } + + private void lookInDirection(Vec3 direction) { + if (direction.lengthSqr() <= 1.0E-4D) { + return; + } + float yaw = (float) (Mth.atan2(direction.z, direction.x) * (180.0D / Math.PI)) - 90.0F; + this.setYRot(yaw); + this.yBodyRot = yaw; + this.yHeadRot = yaw; + this.setYHeadRot(yaw); + } + + @Nullable + private LivingEntity findNearbyLivingEntity(UUID uuid) { + if (!(this.level() instanceof ServerLevel serverLevel)) { + return null; + } + Entity entity = serverLevel.getEntity(uuid); + return entity instanceof LivingEntity living ? living : null; + } + + private List getFrontTargets(double range, double inflation) { + AABB box = this.getBoundingBox().expandTowards(this.getLookAngle().scale(range)).inflate(inflation); + Vec3 forward = this.getLookAngle().multiply(1.0D, 0.0D, 1.0D); + return this.level().getEntitiesOfClass(LivingEntity.class, box, this::canHarm).stream() + .filter(entity -> { + Vec3 toTarget = entity.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D); + if (forward.lengthSqr() < 1.0E-4D || toTarget.lengthSqr() < 1.0E-4D) { + return true; + } + return forward.normalize().dot(toTarget.normalize()) >= 0.15D; + }) + .toList(); + } + + @Nullable + private LivingEntity findFrontTarget(double range, double inflation) { + List targets = this.getFrontTargets(range, inflation); + return targets.isEmpty() ? null : targets.getFirst(); + } + + private boolean shouldShowBossBar() { + return !this.isTame(); + } + + @Override + public void startSeenByPlayer(ServerPlayer player) { + super.startSeenByPlayer(player); + if (this.shouldShowBossBar()) { + this.bossEvent.addPlayer(player); + } + } + + @Override + public void stopSeenByPlayer(ServerPlayer player) { + super.stopSeenByPlayer(player); + this.bossEvent.removePlayer(player); + } + + @Override + public void setCustomName(@Nullable Component name) { + super.setCustomName(name); + this.bossEvent.setName(this.getDisplayName()); + } + + @Override + public void addAdditionalSaveData(CompoundTag tag) { + super.addAdditionalSaveData(tag); + tag.putBoolean("Saddled", this.hasSaddle()); + tag.putBoolean("KnockedDown", this.isKnockedDown()); + tag.putBoolean("Flying", this.isFlying()); + tag.putInt("AnimationState", this.getAnimationState()); + tag.putInt("ChargeCooldown", this.chargeCooldown); + tag.putInt("AttackCooldown", this.attackCooldown); + tag.putBoolean("CombatCryDone", this.combatCryDone); + } + + @Override + public void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); + this.setSaddled(tag.getBoolean("Saddled")); + this.setKnockedDown(tag.getBoolean("KnockedDown")); + this.setFlying(tag.getBoolean("Flying")); + this.setAnimationState(tag.contains("AnimationState") ? tag.getInt("AnimationState") : ANIM_IDLE); + this.chargeCooldown = tag.getInt("ChargeCooldown"); + this.attackCooldown = tag.getInt("AttackCooldown"); + this.combatCryDone = tag.getBoolean("CombatCryDone"); + if (this.isKnockedDown() && this.getAnimationState() != ANIM_KNOCKED_DOWN_IDLE) { + this.setAnimationState(ANIM_KNOCKED_DOWN_IDLE); + } + } + + @Override + protected SoundEvent getAmbientSound() { + return this.isKnockedDown() ? null : SoundEvents.RAVAGER_AMBIENT; + } + + @Override + protected SoundEvent getHurtSound(DamageSource damageSource) { + return SoundEvents.RAVAGER_HURT; + } + + @Override + protected SoundEvent getDeathSound() { + return SoundEvents.RAVAGER_DEATH; + } + + @Override + protected float getSoundVolume() { + return 1.3F; + } + + @Override + protected void playStepSound(BlockPos pos, BlockState state) { + this.playSound(SoundEvents.RAVAGER_STEP, 0.8F, 0.8F); + } + + @Override + @Nullable + public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { + return null; + } + + private final class HerculesCombatGoal extends Goal { + HerculesCombatGoal() { + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + return !HerculesBeetleEntity.this.isTame() + && !HerculesBeetleEntity.this.isKnockedDown() + && HerculesBeetleEntity.this.getTarget() != null; + } + + @Override + public boolean canContinueToUse() { + LivingEntity target = HerculesBeetleEntity.this.getTarget(); + return target != null && target.isAlive() && !HerculesBeetleEntity.this.isTame() && !HerculesBeetleEntity.this.isKnockedDown(); + } + + @Override + public void tick() { + LivingEntity target = HerculesBeetleEntity.this.getTarget(); + if (target == null) { + return; + } + + HerculesBeetleEntity.this.getLookControl().setLookAt(target, 30.0F, 20.0F); + + if (!HerculesBeetleEntity.this.combatCryDone && HerculesBeetleEntity.this.cryCooldown <= 0 && !HerculesBeetleEntity.this.isInBusyAnimation()) { + HerculesBeetleEntity.this.startCry(); + return; + } + + if (HerculesBeetleEntity.this.isInBusyAnimation() || HerculesBeetleEntity.this.chargeTicksRemaining > 0) { + return; + } + + double distanceSqr = HerculesBeetleEntity.this.distanceToSqr(target); + double verticalDelta = Math.abs(target.getY() - HerculesBeetleEntity.this.getY()); + + if (!HerculesBeetleEntity.this.isFlying() && (verticalDelta > 5.0D || HerculesBeetleEntity.this.fallDistance > 4.0F)) { + HerculesBeetleEntity.this.requestFlightStart(); + } else if (HerculesBeetleEntity.this.isFlying() && HerculesBeetleEntity.this.onGround() && distanceSqr < 25.0D && verticalDelta < 1.5D) { + HerculesBeetleEntity.this.requestFlightStop(); + } + + if (HerculesBeetleEntity.this.chargeCooldown <= 0 + && distanceSqr >= CHARGE_TRIGGER_RANGE * CHARGE_TRIGGER_RANGE + && distanceSqr <= 36.0D * 36.0D + && HerculesBeetleEntity.this.random.nextFloat() < 0.015F) { + HerculesBeetleEntity.this.startCharge(target.position().subtract(HerculesBeetleEntity.this.position()), MAX_CHARGE_TICKS, false); + return; + } + + double attackRange = HerculesBeetleEntity.this.isFlying() ? FLY_ATTACK_RANGE : ATTACK_RANGE; + if (HerculesBeetleEntity.this.attackCooldown <= 0 && distanceSqr <= attackRange * attackRange) { + HerculesBeetleEntity.this.startRegularAttack(HerculesBeetleEntity.this.isFlying(), target); + return; + } + + double speed = HerculesBeetleEntity.this.isFlying() ? 1.2D : 1.0D; + HerculesBeetleEntity.this.getNavigation().moveTo(target, speed); + } + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java index 5c46a8ca..4bf9242e 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/StinkBugEntity.java @@ -15,6 +15,7 @@ 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.PanicGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; import net.minecraft.world.entity.animal.Animal; @@ -38,9 +39,12 @@ public class StinkBugEntity extends Animal implements GeoEntity { private static final RawAnimation FART_ANIM = RawAnimation.begin().thenPlay("fart"); private static final int FART_ANIMATION_TICKS = 30; private static final int STINK_BURST_PARTICLES = 32; + private static final double STINK_BURST_RADIUS = 8.0D; + private static final int STINK_BURST_DURATION_TICKS = 1200; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private int fartAnimationTicks; + private boolean pendingFartBurst; public StinkBugEntity(EntityType entityType, Level level) { super(entityType, level); @@ -65,9 +69,10 @@ public class StinkBugEntity extends Animal implements GeoEntity { @Override protected void registerGoals() { this.goalSelector.addGoal(0, new FloatGoal(this)); - this.goalSelector.addGoal(1, new WaterAvoidingRandomStrollGoal(this, 0.9D)); - this.goalSelector.addGoal(2, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(3, new RandomLookAroundGoal(this)); + this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); + this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.9D)); + this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); } @Override @@ -76,15 +81,20 @@ public class StinkBugEntity extends Animal implements GeoEntity { if (this.fartAnimationTicks > 0) { this.fartAnimationTicks--; + if (this.fartAnimationTicks == 0 && this.pendingFartBurst && !this.level().isClientSide()) { + StinkyBehavior.emitBurst(this, STINK_BURST_PARTICLES); + StinkyBehavior.applyBurstStinkyEffect(this, STINK_BURST_RADIUS, STINK_BURST_DURATION_TICKS); + this.pendingFartBurst = false; + } } } @Override public boolean hurt(DamageSource source, float amount) { boolean hurt = super.hurt(source, amount); - if (hurt) { + if (hurt && !this.pendingFartBurst) { this.triggerFartAnimation(); - StinkyBehavior.emitBurst(this, STINK_BURST_PARTICLES); + this.pendingFartBurst = true; } return hurt; } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/ToreterrorEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/ToreterrorEntity.java index dc5b4a72..ce093a57 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/entity/ToreterrorEntity.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/ToreterrorEntity.java @@ -163,6 +163,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity { @Override protected void registerGoals() { + this.goalSelector.addGoal(0, new ToreterrorFloatGoal()); this.goalSelector.addGoal(1, new ToreterrorRangedGoal()); this.goalSelector.addGoal(2, new ToreterrorJumpGoal()); this.goalSelector.addGoal(3, new ToreterrorSpinGoal()); @@ -822,6 +823,28 @@ public class ToreterrorEntity extends Monster implements GeoEntity { } } + private final class ToreterrorFloatGoal extends Goal { + ToreterrorFloatGoal() { + this.setFlags(EnumSet.of(Flag.JUMP)); + } + + @Override + public boolean canUse() { + return ToreterrorEntity.this.isInWater() && !ToreterrorEntity.this.onGround(); + } + + @Override + public boolean canContinueToUse() { + return canUse(); + } + + @Override + public void tick() { + Vec3 vel = ToreterrorEntity.this.getDeltaMovement(); + ToreterrorEntity.this.setDeltaMovement(vel.x, Math.min(vel.y + 0.08D, 0.2D), vel.z); + } + } + private final class ToreterrorSpinGoal extends Goal { ToreterrorSpinGoal() { this.setFlags(EnumSet.of(Flag.MOVE)); diff --git a/common/src/main/java/com/craisinlord/antarchy/content/entity/flying_squirrel/FlyingSquirrelEntity.java b/common/src/main/java/com/craisinlord/antarchy/content/entity/flying_squirrel/FlyingSquirrelEntity.java index d944f823..4bde8a0f 100644 --- a/common/src/main/java/com/craisinlord/antarchy/content/entity/flying_squirrel/FlyingSquirrelEntity.java +++ b/common/src/main/java/com/craisinlord/antarchy/content/entity/flying_squirrel/FlyingSquirrelEntity.java @@ -55,6 +55,7 @@ import software.bernie.geckolib.animation.AnimationState; import software.bernie.geckolib.util.GeckoLibUtil; import java.util.EnumSet; +import java.util.Optional; import java.util.UUID; public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { @@ -71,6 +72,8 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { SynchedEntityData.defineId(FlyingSquirrelEntity.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor TEXTURE_VARIANT = SynchedEntityData.defineId(FlyingSquirrelEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor> SHOULDER_PLAYER_DATA = + SynchedEntityData.defineId(FlyingSquirrelEntity.class, EntityDataSerializers.OPTIONAL_UUID); private static final String PICKUP_ANIM_TICKS_KEY = "PickupAnimTicks"; private static final String TEXTURE_VARIANT_KEY = "TextureVariant"; @@ -227,6 +230,7 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { builder.define(CLIMBING, false); builder.define(PICKING_UP_NUT, false); builder.define(TEXTURE_VARIANT, TEXTURE_VARIANT_REGULAR); + builder.define(SHOULDER_PLAYER_DATA, Optional.empty()); } @Override @@ -376,13 +380,19 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { } if (this.isOnShoulder()) { - if (!(this.getVehicle() instanceof Player shoulderPlayer) - || !shoulderPlayer.isAlive()) { + Player shoulderPlayer = this.getShoulderPlayer(); + if (shoulderPlayer == null || !shoulderPlayer.isAlive()) { this.dismountShoulder(); } else if (this.shoulderSneakGraceTicks <= 0 && shoulderPlayer.isShiftKeyDown()) { this.glideOffShoulder(); } else { + Vec3 shoulderPos = this.getShoulderRidePos(shoulderPlayer); + this.setPos(shoulderPos.x, shoulderPos.y, shoulderPos.z); this.setDeltaMovement(Vec3.ZERO); + this.setYRot(shoulderPlayer.getYHeadRot()); + this.setXRot(shoulderPlayer.getXRot()); + this.yBodyRot = shoulderPlayer.getYHeadRot(); + this.yHeadRot = shoulderPlayer.getYHeadRot(); this.fallDistance = 0.0F; } this.setNoGravity(true); @@ -434,22 +444,25 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { this.tickIdleStallLogging(horizontalSpeed); } + if (this.isOnShoulder() && this.level().isClientSide) { + Player shoulderPlayer = this.getShoulderPlayer(); + if (shoulderPlayer != null) { + Vec3 shoulderPos = this.getShoulderRidePos(shoulderPlayer); + this.setPos(shoulderPos.x, shoulderPos.y, shoulderPos.z); + this.setDeltaMovement(Vec3.ZERO); + this.setYRot(shoulderPlayer.getYHeadRot()); + this.setXRot(shoulderPlayer.getXRot()); + this.yBodyRot = shoulderPlayer.getYHeadRot(); + this.yHeadRot = shoulderPlayer.getYHeadRot(); + this.fallDistance = 0.0F; + } + } this.setNoGravity(this.isGliding() || this.isClimbingTrunk() || this.isClimbingToShoulder() || this.isOnShoulder()); } @Override public void rideTick() { super.rideTick(); - if (this.getVehicle() instanceof Player player) { - Vec3 shoulderPos = this.getShoulderRidePos(player); - this.setPos(shoulderPos.x, shoulderPos.y, shoulderPos.z); - this.setDeltaMovement(Vec3.ZERO); - this.setYRot(player.getYHeadRot()); - this.setXRot(player.getXRot()); - this.yBodyRot = player.getYHeadRot(); - this.yHeadRot = player.getYHeadRot(); - this.fallDistance = 0.0F; - } } @Override @@ -703,7 +716,14 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { } public boolean isOnShoulder() { - return this.getVehicle() instanceof Player; + return this.entityData.get(SHOULDER_PLAYER_DATA).isPresent(); + } + + @Nullable + private Player getShoulderPlayer() { + return this.entityData.get(SHOULDER_PLAYER_DATA) + .map(uuid -> this.level().getPlayerByUUID(uuid)) + .orElse(null); } public boolean isClimbingToShoulder() { @@ -725,10 +745,9 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { } private void dismountShoulder() { - Entity vehicle = this.getVehicle(); - if (vehicle != null) { - this.stopRiding(); - Vec3 dismountPos = vehicle.position().add(0.0D, 0.1D, 0.0D); + Player shoulderPlayer = this.getShoulderPlayer(); + if (shoulderPlayer != null) { + Vec3 dismountPos = shoulderPlayer.position().add(0.0D, 0.1D, 0.0D); this.setPos(dismountPos.x, dismountPos.y, dismountPos.z); } this.clearShoulderMountState(); @@ -737,6 +756,7 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { } private void clearShoulderMountState() { + this.entityData.set(SHOULDER_PLAYER_DATA, Optional.empty()); this.shoulderPlayerId = null; this.shoulderClimbStartPos = null; this.climbingToShoulderTicks = 0; @@ -746,16 +766,15 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { } private void glideOffShoulder() { - Entity vehicle = this.getVehicle(); - if (!(vehicle instanceof Player player)) { + Player player = this.getShoulderPlayer(); + if (player == null) { this.dismountShoulder(); return; } Vec3 launchPos = this.getShoulderRidePos(player); - this.stopRiding(); - this.setPos(launchPos.x, launchPos.y, launchPos.z); this.clearShoulderMountState(); + this.setPos(launchPos.x, launchPos.y, launchPos.z); this.fallDistance = 0.0F; BlockPos glideTarget = this.findFallGlideTarget(); @@ -815,7 +834,7 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity { this.shoulderPlayerId = null; this.shoulderSneakGraceTicks = SHOULDER_SNEAK_GRACE_TICKS; this.setClimbingTrunk(false); - this.startRiding(player, true); + this.entityData.set(SHOULDER_PLAYER_DATA, Optional.of(player.getUUID())); this.setDeltaMovement(Vec3.ZERO); this.fallDistance = 0.0F; } diff --git a/common/src/main/java/com/craisinlord/antarchy/content/item/CheepOnAStickItem.java b/common/src/main/java/com/craisinlord/antarchy/content/item/CheepOnAStickItem.java deleted file mode 100644 index 7dd0776f..00000000 --- a/common/src/main/java/com/craisinlord/antarchy/content/item/CheepOnAStickItem.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import net.minecraft.world.item.Item; - -public class CheepOnAStickItem extends Item { - public CheepOnAStickItem(Properties properties) { - super(properties); - } -} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/menu/DorrieMenu.java b/common/src/main/java/com/craisinlord/antarchy/content/menu/DorrieMenu.java deleted file mode 100644 index f9b3ff02..00000000 --- a/common/src/main/java/com/craisinlord/antarchy/content/menu/DorrieMenu.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.craisinlord.antarchy.content.menu; - -import net.minecraft.world.Container; -import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; - -import java.util.function.Supplier; - -public class DorrieMenu extends AbstractContainerMenu { - // Set by the Fabric platform during registration so common entity code can open the menu. - @SuppressWarnings("unchecked") - public static Supplier> TYPE_SUPPLIER = () -> { - throw new IllegalStateException("DorrieMenu.TYPE_SUPPLIER accessed before registration"); - }; - - private static final int EQUIPMENT_SLOTS = 1; - private static final int PLAYER_INV_ROWS = 3; - private static final int PLAYER_INV_COLS = 9; - - // GUI layout constants (relative to background image top-left) - public static final int SADDLE_SLOT_X = 80; - public static final int SADDLE_SLOT_Y = 18; - public static final int PLAYER_INV_X = 8; - public static final int PLAYER_INV_Y = 48; - public static final int HOTBAR_Y = 106; - - private final Container equipment; - - // Server-side constructor — receives the real Dorrie inventory. - public DorrieMenu(int syncId, Inventory playerInventory, Container equipment) { - super(TYPE_SUPPLIER.get(), syncId); - this.equipment = equipment; - checkContainerSize(equipment, EQUIPMENT_SLOTS); - - // Slot 0: saddle - this.addSlot(new Slot(equipment, 0, SADDLE_SLOT_X, SADDLE_SLOT_Y) { - @Override - public boolean mayPlace(ItemStack stack) { - return stack.is(Items.SADDLE); - } - - @Override - public int getMaxStackSize() { - return 1; - } - }); - - // Player inventory (3 × 9) - for (int row = 0; row < PLAYER_INV_ROWS; row++) { - for (int col = 0; col < PLAYER_INV_COLS; col++) { - this.addSlot(new Slot( - playerInventory, - col + row * PLAYER_INV_COLS + PLAYER_INV_COLS, - PLAYER_INV_X + col * 18, - PLAYER_INV_Y + row * 18 - )); - } - } - - // Hotbar (9) - for (int col = 0; col < PLAYER_INV_COLS; col++) { - this.addSlot(new Slot(playerInventory, col, PLAYER_INV_X + col * 18, HOTBAR_Y)); - } - } - - // Client-side factory — syncs slot data from server. - public static DorrieMenu clientCreate(int syncId, Inventory playerInventory) { - return new DorrieMenu(syncId, playerInventory, new SimpleContainer(EQUIPMENT_SLOTS)); - } - - @Override - public boolean stillValid(Player player) { - return this.equipment.stillValid(player); - } - - @Override - public ItemStack quickMoveStack(Player player, int index) { - ItemStack result = ItemStack.EMPTY; - Slot slot = this.slots.get(index); - if (!slot.hasItem()) return result; - - ItemStack stack = slot.getItem(); - result = stack.copy(); - - int equipEnd = EQUIPMENT_SLOTS; - int invEnd = equipEnd + PLAYER_INV_ROWS * PLAYER_INV_COLS; - int totalSlots = invEnd + PLAYER_INV_COLS; - - if (index < equipEnd) { - // Equipment slot → player inventory - if (!this.moveItemStackTo(stack, equipEnd, totalSlots, true)) { - return ItemStack.EMPTY; - } - } else { - // Player inventory/hotbar → try equipment slot first - if (stack.is(Items.SADDLE) && this.slots.get(0).mayPlace(stack)) { - if (!this.moveItemStackTo(stack, 0, equipEnd, false)) { - return ItemStack.EMPTY; - } - } else if (index < invEnd) { - // Inventory → hotbar - if (!this.moveItemStackTo(stack, invEnd, totalSlots, false)) { - return ItemStack.EMPTY; - } - } else { - // Hotbar → inventory - if (!this.moveItemStackTo(stack, equipEnd, invEnd, false)) { - return ItemStack.EMPTY; - } - } - } - - if (stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - - if (stack.getCount() == result.getCount()) return ItemStack.EMPTY; - slot.onTake(player, stack); - return result; - } -} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakePayload.java b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakePayload.java new file mode 100644 index 00000000..6e8a14dc --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakePayload.java @@ -0,0 +1,20 @@ +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 HerculesBeetleImpactShakePayload(int durationTicks) implements CustomPacketPayload { + public static final Type TYPE = + new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "hercules_beetle_impact_shake")); + public static final StreamCodec STREAM_CODEC = + ByteBufCodecs.VAR_INT.map(HerculesBeetleImpactShakePayload::new, HerculesBeetleImpactShakePayload::durationTicks); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakeSync.java b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakeSync.java new file mode 100644 index 00000000..3f415f7c --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleImpactShakeSync.java @@ -0,0 +1,22 @@ +package com.craisinlord.antarchy.content.network; + +import java.util.function.BiConsumer; +import net.minecraft.server.level.ServerPlayer; + +public final class HerculesBeetleImpactShakeSync { + private static final BiConsumer NOOP = (player, ticks) -> { + }; + + private static volatile BiConsumer sink = NOOP; + + private HerculesBeetleImpactShakeSync() { + } + + public static void setSink(BiConsumer sink) { + HerculesBeetleImpactShakeSync.sink = sink == null ? NOOP : sink; + } + + public static void send(ServerPlayer player, int ticks) { + sink.accept(player, ticks); + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleJumpInputPayload.java b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleJumpInputPayload.java new file mode 100644 index 00000000..a356b2c8 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleJumpInputPayload.java @@ -0,0 +1,23 @@ +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 HerculesBeetleJumpInputPayload(boolean pressing) implements CustomPacketPayload { + public static final Type TYPE = + new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "hercules_beetle_jump_input")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + ByteBufCodecs.BOOL, HerculesBeetleJumpInputPayload::pressing, + HerculesBeetleJumpInputPayload::new + ); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedAttackPayload.java b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedAttackPayload.java new file mode 100644 index 00000000..76b5e8d3 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedAttackPayload.java @@ -0,0 +1,19 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record HerculesBeetleMountedAttackPayload() implements CustomPacketPayload { + public static final Type TYPE = + new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "hercules_beetle_mounted_attack")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(new HerculesBeetleMountedAttackPayload()); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedChargePayload.java b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedChargePayload.java new file mode 100644 index 00000000..292532ef --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/content/network/HerculesBeetleMountedChargePayload.java @@ -0,0 +1,20 @@ +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 HerculesBeetleMountedChargePayload(int chargeTicks) implements CustomPacketPayload { + public static final Type TYPE = + new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "hercules_beetle_mounted_charge")); + public static final StreamCodec STREAM_CODEC = + ByteBufCodecs.VAR_INT.map(HerculesBeetleMountedChargePayload::new, HerculesBeetleMountedChargePayload::chargeTicks); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java b/common/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java new file mode 100644 index 00000000..47605151 --- /dev/null +++ b/common/src/main/java/com/craisinlord/antarchy/mixins/client/MountedVehicleCameraMixin.java @@ -0,0 +1,49 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.entity.DorrieEntity; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import net.minecraft.client.Camera; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.BlockGetter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArgs; +import org.spongepowered.asm.mixin.injection.invoke.arg.Args; + +@Mixin(Camera.class) +public abstract class MountedVehicleCameraMixin { + @Shadow + private Entity entity; + + @ModifyArgs( + method = "setup", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V") + ) + private void antarchy$adjustMountedCameraOffset( + Args args, + BlockGetter level, + Entity entity, + boolean detached, + boolean mirrored, + float partialTick + ) { + if (!detached || this.entity == null || this.entity.getVehicle() == null) { + return; + } + + Entity vehicle = this.entity.getVehicle(); + if (vehicle instanceof HerculesBeetleEntity) { + args.set(0, (Float) args.get(0) * 1.45F); + args.set(1, (Float) args.get(1) + 0.2F); + args.set(2, (Float) args.get(2) + 0.45F); + return; + } + + if (vehicle instanceof DorrieEntity) { + args.set(0, (Float) args.get(0) * 1.6F); + args.set(1, (Float) args.get(1) + 0.15F); + args.set(2, (Float) args.get(2) + 0.55F); + } + } +} diff --git a/common/src/main/resources/antarchy-common.mixins.json b/common/src/main/resources/antarchy-common.mixins.json index 1dea3fec..24677f3c 100644 --- a/common/src/main/resources/antarchy-common.mixins.json +++ b/common/src/main/resources/antarchy-common.mixins.json @@ -51,6 +51,7 @@ "client.ElythiaSkyMixin", "client.ElytraModelAccessor", "client.LightningBoltRendererMixin", + "client.MountedVehicleCameraMixin", "client.ParalyzedMouseTurnMixin", "client.ScreenEffectRendererFireVisualMixin", "client.SplashManagerMixin", diff --git a/common/src/main/resources/assets/antarchy/lang/en_us.json b/common/src/main/resources/assets/antarchy/lang/en_us.json index 3820743c..2832695d 100644 --- a/common/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common/src/main/resources/assets/antarchy/lang/en_us.json @@ -766,12 +766,13 @@ "death.attack.water_soaked.player": "%1$s was super soaked to death by %2$s", "entity.antarchy.cheep": "Cheep", "entity.antarchy.dorrie": "Dorrie", + "entity.antarchy.hercules_beetle": "Hercules Beetle", "item.antarchy.cheep": "Cheep", - "item.antarchy.cheep_on_a_stick": "Cheep on a Stick", "item.antarchy.cheep_spawn_egg": "Cheep Spawn Egg", "item.antarchy.dorrie_spawn_egg": "Dorrie Spawn Egg", - "container.antarchy.dorrie": "Dorrie", + "item.antarchy.hercules_beetle_spawn_egg": "Hercules Beetle Spawn Egg", "key.antarchy.dorrie_charge_jump": "Dorrie: Charge Jump", + "key.antarchy.hercules_beetle_charge": "Hercules Beetle: Charge", "entity.antarchy.reverie": "Reverie", "biome.antarchy.thoraxis": "Thoraxis", "biome.antarchy.nightmare_wastes": "Nightmare Wastes", diff --git a/common/src/main/resources/assets/antarchy/models/item/hercules_beetle_spawn_egg.json b/common/src/main/resources/assets/antarchy/models/item/hercules_beetle_spawn_egg.json new file mode 100644 index 00000000..ddd15597 --- /dev/null +++ b/common/src/main/resources/assets/antarchy/models/item/hercules_beetle_spawn_egg.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/template_spawn_egg" +} diff --git a/common/src/main/resources/data/antarchy/dimension_type/cavaryn.json b/common/src/main/resources/data/antarchy/dimension_type/cavaryn.json index aac92d86..5aed0ffe 100644 --- a/common/src/main/resources/data/antarchy/dimension_type/cavaryn.json +++ b/common/src/main/resources/data/antarchy/dimension_type/cavaryn.json @@ -9,7 +9,7 @@ "logical_height": 192, "infiniburn": "#minecraft:infiniburn_overworld", "effects": "minecraft:overworld", - "ambient_light": 0.01, + "ambient_light": 0.015, "piglin_safe": false, "has_raids": false, "monster_spawn_light_level": 0, diff --git a/common/src/main/resources/data/antarchy/tags/block/hercules_beetle_charge_immune_blocks.json b/common/src/main/resources/data/antarchy/tags/block/hercules_beetle_charge_immune_blocks.json new file mode 100644 index 00000000..5234c475 --- /dev/null +++ b/common/src/main/resources/data/antarchy/tags/block/hercules_beetle_charge_immune_blocks.json @@ -0,0 +1,18 @@ +{ + "replace": false, + "values": [ + "minecraft:bedrock", + "minecraft:obsidian", + "minecraft:crying_obsidian", + "minecraft:barrier", + "minecraft:command_block", + "minecraft:chain_command_block", + "minecraft:repeating_command_block", + "minecraft:structure_block", + "minecraft:jigsaw", + "minecraft:structure_void", + "minecraft:end_portal_frame", + "minecraft:end_portal", + "minecraft:end_gateway" + ] +} diff --git a/common/src/main/resources/data/antarchy/tags/item/hercules_beetle_food.json b/common/src/main/resources/data/antarchy/tags/item/hercules_beetle_food.json new file mode 100644 index 00000000..0f2f4352 --- /dev/null +++ b/common/src/main/resources/data/antarchy/tags/item/hercules_beetle_food.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:glow_berries" + ] +} diff --git a/common/src/main/resources/data/antarchy/worldgen/biome/butterfly_fields.json b/common/src/main/resources/data/antarchy/worldgen/biome/butterfly_fields.json index ea553ae8..d5b02036 100644 --- a/common/src/main/resources/data/antarchy/worldgen/biome/butterfly_fields.json +++ b/common/src/main/resources/data/antarchy/worldgen/biome/butterfly_fields.json @@ -18,10 +18,6 @@ } }, "spawn_costs": { - "antarchy:brutalfly": { - "energy_budget": 0.3, - "charge": 1.2 - } }, "carvers": { "air": [], diff --git a/common/src/main/resources/data/antarchy/worldgen/biome/cavaryn.json b/common/src/main/resources/data/antarchy/worldgen/biome/cavaryn.json index 32b4ea7d..4c7e0c44 100644 --- a/common/src/main/resources/data/antarchy/worldgen/biome/cavaryn.json +++ b/common/src/main/resources/data/antarchy/worldgen/biome/cavaryn.json @@ -22,7 +22,12 @@ } } }, - "spawn_costs": {}, + "spawn_costs": { + "antarchy:hercules_beetle": { + "energy_budget": 0.1, + "charge": 15 + } + }, "carvers": { "air": [], "liquid": [] @@ -61,6 +66,12 @@ "weight": 95, "minCount": 2, "maxCount": 5 + }, + { + "type": "antarchy:hercules_beetle", + "weight": 5, + "minCount": 1, + "maxCount": 1 } ], "creature": [ diff --git a/common/src/main/resources/data/minecraft/tags/entity_type/arthropod.json b/common/src/main/resources/data/minecraft/tags/entity_type/arthropod.json index 23a5c7e0..520257ad 100644 --- a/common/src/main/resources/data/minecraft/tags/entity_type/arthropod.json +++ b/common/src/main/resources/data/minecraft/tags/entity_type/arthropod.json @@ -11,6 +11,7 @@ "antarchy:jumpy_bug", "antarchy:spit_bug", "antarchy:stink_bug", + "antarchy:hercules_beetle", "antarchy:creeping_horror", "antarchy:lurking_terror", "antarchy:scorpion", diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java index 185f2508..65637c05 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java @@ -114,6 +114,7 @@ import com.craisinlord.antarchy.content.item.DiamondMinecartItem; import com.craisinlord.antarchy.content.item.ReverieBottleItem; import com.craisinlord.antarchy.content.item.ScorpionWhipItem; import com.craisinlord.antarchy.content.item.ScorpionWhipTetherSync; +import com.craisinlord.antarchy.content.network.HerculesBeetleImpactShakeSync; import com.craisinlord.antarchy.content.item.SizeRayItem; import com.craisinlord.antarchy.content.item.SquidzookaItem; import com.craisinlord.antarchy.content.item.SimpleToolTier; @@ -1820,14 +1821,19 @@ public final class AntarchyFabricContent { .sized(2.4F, 2.0F) .clientTrackingRange(10) .build("dorrie")); + public static final DeferredHolder, EntityType> HERCULES_BEETLE = ENTITY_TYPES.register("hercules_beetle", + () -> EntityType.Builder.of(com.craisinlord.antarchy.content.entity.HerculesBeetleEntity::new, MobCategory.MONSTER) + .sized(3.0F, 4.0F) + .clientTrackingRange(12) + .build("hercules_beetle")); public static final DeferredItem CHEEP_ITEM = ITEMS.register("cheep", () -> new Item(new Item.Properties().food(new FoodProperties.Builder().nutrition(2).saturationModifier(0.2F).build()))); - public static final DeferredItem CHEEP_ON_A_STICK = ITEMS.register("cheep_on_a_stick", - () -> new com.craisinlord.antarchy.content.item.CheepOnAStickItem(new Item.Properties().stacksTo(1).durability(25))); public static final DeferredItem CHEEP_SPAWN_EGG = ITEMS.register("cheep_spawn_egg", () -> new DeferredSpawnEggItem(CHEEP, 0xFF00AA, 0x00FF44, new Item.Properties())); public static final DeferredItem DORRIE_SPAWN_EGG = ITEMS.register("dorrie_spawn_egg", () -> new DeferredSpawnEggItem(DORRIE, 0x1B91B3, 0xC0E8FF, new Item.Properties())); + public static final DeferredItem HERCULES_BEETLE_SPAWN_EGG = ITEMS.register("hercules_beetle_spawn_egg", + () -> new DeferredSpawnEggItem(HERCULES_BEETLE, 0x6B1F2A, 0xD4AF37, new Item.Properties().rarity(Rarity.RARE))); public static final DeferredHolder ANTARCHY_TAB = CREATIVE_MODE_TABS.register("antarchy", () -> CreativeModeTab.builder(CreativeModeTab.Row.TOP, 0) @@ -1917,6 +1923,7 @@ public final class AntarchyFabricContent { FabricDefaultAttributeRegistry.register(LURKING_TERROR.get(), LurkingTerrorEntity.createAttributes().build()); FabricDefaultAttributeRegistry.register(CHEEP.get(), com.craisinlord.antarchy.content.entity.CheepEntity.createAttributes().build()); FabricDefaultAttributeRegistry.register(DORRIE.get(), com.craisinlord.antarchy.content.entity.DorrieEntity.createAttributes().build()); + FabricDefaultAttributeRegistry.register(HERCULES_BEETLE.get(), com.craisinlord.antarchy.content.entity.HerculesBeetleEntity.createAttributes().build()); FabricDefaultAttributeRegistry.register(RED_ANT.get(), buildAntAttributes( AntarchySettings.redAntHealth(), @@ -2069,10 +2076,10 @@ public final class AntarchyFabricContent { "bomber_spawn_egg", "jumpy_bug_spawn_egg", "spit_bug_spawn_egg", "stink_bug_spawn_egg", "cloud_shark_spawn_egg", "kraken_spawn_egg", "missile_squid_spawn_egg", "octopus_bomb_spawn_egg", "nightmare_spawn_egg", "bed_bug_spawn_egg", "lucid_spawn_egg", "scorpion_spawn_egg", "basilisk_spawn_egg", "emperor_scorpion_spawn_egg", "toreterror_spawn_egg", + "hercules_beetle_spawn_egg", "creeping_horror_spawn_egg", "lurking_terror_spawn_egg" -> 90; case "stink_bug", "chiten" -> 22; case "cheep" -> 21; - case "cheep_on_a_stick" -> 52; case "water_cannon" -> 52; case "primordial_helmet", "primordial_chestplate", "primordial_leggings", "primordial_boots" -> 53; case "jumpy_boots" -> 54; @@ -2359,6 +2366,7 @@ public final class AntarchyFabricContent { // Wire common-module accessors after all registrations are complete. AntarchyObjects.CHEEP_ITEM = () -> CHEEP_ITEM.get(); + AntarchyObjects.setHerculesBeetle(() -> HERCULES_BEETLE.get()); AntarchySoundEvents.bind( SQUIDZOOKA_FIRE, @@ -2588,6 +2596,7 @@ public final class AntarchyFabricContent { LucidEyeProjectileEntity.invertedEffectSupplier = () -> mobEffectHolder(INVERTED); ScorpionWhipTetherSync.setSink(AntarchyFabricNetworking::syncScorpionWhipTether); + HerculesBeetleImpactShakeSync.setSink((player, ticks) -> net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.send(player, new com.craisinlord.antarchy.content.network.HerculesBeetleImpactShakePayload(ticks))); BloodCrystalKatanaItem.setTrailCallback(AntarchyFabricNetworking::syncKatanaTrail); com.craisinlord.antarchy.content.gravity.AntarchyGravityApi.setSyncDispatcher(AntarchyFabricNetworking::syncGravityEntity); AntarchyFabricEvents.register(); diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java index 5a7617ae..a3267807 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.fabric.client; import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; import com.craisinlord.antarchy.content.client.renderer.*; import com.craisinlord.antarchy.content.client.particle.*; import com.craisinlord.antarchy.content.client.renderer.AntiwaterFluidRenderer; @@ -49,10 +50,6 @@ public final class AntarchyFabricClientBootstrap { public static void register() { AntarchyKeyBindings.register(); - net.minecraft.client.gui.screens.MenuScreens.register( - com.craisinlord.antarchy.fabric.AntarchyFabricContent.DORRIE_MENU.get(), - com.craisinlord.antarchy.content.client.screen.DorrieScreen::new - ); registerRenderers(); registerEntityLayers(); registerColors(); @@ -119,6 +116,7 @@ public final class AntarchyFabricClientBootstrap { EntityRendererRegistry.register(AntarchyFabricContent.LURKING_TERROR.get(), LurkingTerrorRenderer::new); EntityRendererRegistry.register(AntarchyFabricContent.CHEEP.get(), com.craisinlord.antarchy.content.client.renderer.CheepRenderer::new); EntityRendererRegistry.register(AntarchyFabricContent.DORRIE.get(), com.craisinlord.antarchy.content.client.renderer.DorrieRenderer::new); + EntityRendererRegistry.register(AntarchyFabricContent.HERCULES_BEETLE.get(), com.craisinlord.antarchy.content.client.renderer.HerculesBeetleRenderer::new); EntityModelLayerRegistry.registerModelLayer(OuranwoodBoatRenderer.boatLayer(), BoatModel::createBodyModel); EntityModelLayerRegistry.registerModelLayer(OuranwoodBoatRenderer.chestBoatLayer(), ChestBoatModel::createBodyModel); @@ -321,6 +319,7 @@ public final class AntarchyFabricClientBootstrap { private static void registerClientCallbacks() { ClientTickEvents.END_CLIENT_TICK.register(client -> { + HerculesBeetleImpactShakeClientState.tick(); if (client.player != null) { ParalyzedClientHandler.clampPlayerInput(client.player); } @@ -332,6 +331,7 @@ public final class AntarchyFabricClientBootstrap { BloodCrystalKatanaTrailHandler.tick(); ScorpionWhipTetherRenderHandler.tick(); DorrieJumpClientHandler.tick(); + HerculesBeetleClientHandler.tick(); }); GravityGunClientHandler.register(); diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java index 57655643..592f64ea 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java @@ -24,11 +24,19 @@ public final class AntarchyKeyBindings { CATEGORY ); + public static final KeyMapping HERCULES_BEETLE_CHARGE = new KeyMapping( + "key.antarchy.hercules_beetle_charge", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_LEFT_CONTROL, + CATEGORY + ); + private AntarchyKeyBindings() {} public static void register() { KeyBindingHelper.registerKeyBinding(BRUTALFLY_FLAP); KeyBindingHelper.registerKeyBinding(DORRIE_CHARGE_JUMP); + KeyBindingHelper.registerKeyBinding(HERCULES_BEETLE_CHARGE); } public static boolean isBrutalflyFlapPressed() { @@ -38,4 +46,8 @@ public final class AntarchyKeyBindings { public static boolean isDorrieChargeJumpPressed() { return Minecraft.getInstance().screen == null && DORRIE_CHARGE_JUMP.isDown(); } + + public static boolean isHerculesBeetleChargePressed() { + return Minecraft.getInstance().screen == null && HERCULES_BEETLE_CHARGE.isDown(); + } } diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/HerculesBeetleClientHandler.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/HerculesBeetleClientHandler.java new file mode 100644 index 00000000..53a34842 --- /dev/null +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/client/HerculesBeetleClientHandler.java @@ -0,0 +1,66 @@ +package com.craisinlord.antarchy.fabric.client; + +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import com.craisinlord.antarchy.content.network.HerculesBeetleJumpInputPayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleMountedAttackPayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleMountedChargePayload; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; + +public final class HerculesBeetleClientHandler { + private static final int MAX_CHARGE_TICKS = 40; + + private static boolean wasPressingJump; + private static boolean wasPressingAttack; + private static boolean wasCharging; + private static int chargeTicks; + + private HerculesBeetleClientHandler() { + } + + public static void tick() { + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + if (player == null || mc.screen != null) { + resetAll(); + return; + } + + if (!(player.getVehicle() instanceof HerculesBeetleEntity)) { + resetAll(); + return; + } + + boolean pressingJump = mc.options.keyJump.isDown(); + if (pressingJump != wasPressingJump) { + ClientPlayNetworking.send(new HerculesBeetleJumpInputPayload(pressingJump)); + wasPressingJump = pressingJump; + } + + boolean pressingAttack = mc.options.keyAttack.isDown(); + if (pressingAttack && !wasPressingAttack) { + ClientPlayNetworking.send(new HerculesBeetleMountedAttackPayload()); + } + wasPressingAttack = pressingAttack; + + boolean charging = AntarchyKeyBindings.isHerculesBeetleChargePressed(); + if (charging) { + chargeTicks = Math.min(chargeTicks + 1, MAX_CHARGE_TICKS); + } else if (wasCharging && chargeTicks > 0) { + ClientPlayNetworking.send(new HerculesBeetleMountedChargePayload(chargeTicks)); + chargeTicks = 0; + } + wasCharging = charging; + } + + private static void resetAll() { + if (wasPressingJump) { + ClientPlayNetworking.send(new HerculesBeetleJumpInputPayload(false)); + wasPressingJump = false; + } + wasPressingAttack = false; + wasCharging = false; + chargeTicks = 0; + } +} diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/ToreterrorCameraShakeMixin.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/CameraShakeMixin.java similarity index 64% rename from fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/ToreterrorCameraShakeMixin.java rename to fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/CameraShakeMixin.java index 4189a390..d5d3ec70 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/ToreterrorCameraShakeMixin.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/CameraShakeMixin.java @@ -1,7 +1,8 @@ package com.craisinlord.antarchy.fabric.mixin.client; import com.craisinlord.antarchy.content.entity.ToreterrorEntity; -import com.craisinlord.antarchy.content.item.BigBerthaItem; +import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity; +import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; import net.minecraft.client.Minecraft; @@ -16,14 +17,15 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(GameRenderer.class) -public abstract class ToreterrorCameraShakeMixin { +public abstract class CameraShakeMixin { private static final double MAX_SHAKE_RANGE = 48.0D; private static final int TORETERROR_SHAKE_TICKS = 25; + private static final double NIGHTMARE_SHAKE_RANGE = 32.0D; @Shadow @Final private Minecraft minecraft; @Inject(method = "bobView", at = @At("TAIL")) - private void applyToreterrorShake(PoseStack poseStack, float partialTick, CallbackInfo ci) { + private void applyCameraShake(PoseStack poseStack, float partialTick, CallbackInfo ci) { if (minecraft.level == null || minecraft.player == null) return; Vec3 cameraPos = minecraft.gameRenderer.getMainCamera().getPosition(); @@ -39,8 +41,19 @@ public abstract class ToreterrorCameraShakeMixin { shakeStrength += (float) ((1.0D - distance / MAX_SHAKE_RANGE) * 1.0F); } - if (BigBerthaItem.clientShakeTicks > 0) { - shakeStrength += (float) BigBerthaItem.clientShakeTicks / TORETERROR_SHAKE_TICKS * 2.0F; + for (NightmareEntity nightmare : minecraft.level.getEntitiesOfClass( + NightmareEntity.class, + minecraft.player.getBoundingBox().inflate(NIGHTMARE_SHAKE_RANGE), + entity -> entity.isAlive() && entity.isRoaring() + )) { + double distance = Math.sqrt(cameraPos.distanceToSqr(nightmare.position().add(0.0D, nightmare.getBbHeight() * 0.5D, 0.0D))); + if (distance > NIGHTMARE_SHAKE_RANGE) continue; + shakeStrength += (float) ((1.0D - distance / NIGHTMARE_SHAKE_RANGE) * 1.35D); + } + + int beetleShakeTicks = HerculesBeetleImpactShakeClientState.getTicks(); + if (beetleShakeTicks > 0) { + shakeStrength += (float) beetleShakeTicks / TORETERROR_SHAKE_TICKS * 2.0F; } if (shakeStrength <= 0.0F) return; diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/NightmareCameraShakeMixin.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/NightmareCameraShakeMixin.java deleted file mode 100644 index a70f5e60..00000000 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/mixin/client/NightmareCameraShakeMixin.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.craisinlord.antarchy.fabric.mixin.client; - -import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Axis; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.util.Mth; -import net.minecraft.world.phys.Vec3; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -//TODO FIX ON FABRIC SIDE -@Mixin(GameRenderer.class) -public abstract class NightmareCameraShakeMixin { -// private static final double MAX_SHAKE_RANGE = 32.0D; -// -// @Shadow @Final private Minecraft minecraft; -// -// @Redirect( -// method = "renderLevel", -// at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;bobView(Lcom/mojang/blaze3d/vertex/PoseStack;F)V") -// ) -// private void applyNightmareCameraShake(GameRenderer instance, PoseStack poseStack, float partialTick) { -// instance.bobView(poseStack, partialTick); -// if (minecraft.level == null || minecraft.player == null) { -// return; -// } -// -// Vec3 cameraPos = minecraft.gameRenderer.getMainCamera().getPosition(); -// float shakeStrength = 0.0F; -// -// for (NightmareEntity nightmare : minecraft.level.getEntitiesOfClass( -// NightmareEntity.class, -// minecraft.player.getBoundingBox().inflate(MAX_SHAKE_RANGE), -// e -> e.isAlive() && e.isRoaring() -// )) { -// double distance = Math.sqrt(cameraPos.distanceToSqr(nightmare.position().add(0.0D, nightmare.getBbHeight() * 0.5D, 0.0D))); -// if (distance > MAX_SHAKE_RANGE) { -// continue; -// } -// shakeStrength += (float) ((1.0D - distance / MAX_SHAKE_RANGE) * 1.35D); -// } -// -// if (shakeStrength <= 0.0F) { -// return; -// } -// -// float time = (float) (minecraft.player.tickCount + partialTick); -// float yawOffset = Mth.sin(time * 1.85F) * shakeStrength * 1.8F; -// float pitchOffset = Mth.cos(time * 2.15F) * shakeStrength * 1.45F; -// float rollOffset = Mth.sin(time * 2.65F) * shakeStrength * 1.05F; -// -// poseStack.mulPose(Axis.YP.rotationDegrees(yawOffset)); -// poseStack.mulPose(Axis.XP.rotationDegrees(pitchOffset)); -// poseStack.mulPose(Axis.ZP.rotationDegrees(rollOffset)); -// } -} diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java index 2bb3e573..13f6b682 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java @@ -34,5 +34,7 @@ public final class AntarchyFabricClientNetworking { payload.anchorY(), payload.anchorZ() ))); + ClientPlayNetworking.registerGlobalReceiver(HerculesBeetleImpactShakePayload.TYPE, (payload, context) -> + context.client().execute(() -> AntarchyFabricNetworking.triggerHerculesBeetleImpactShake(payload.durationTicks()))); } } diff --git a/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java b/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java index 2774ce91..fb427c2b 100644 --- a/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java +++ b/fabric/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java @@ -1,12 +1,15 @@ package com.craisinlord.antarchy.fabric.network; import com.craisinlord.antarchy.content.entity.DiamondMinecartEntity; +import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; import com.craisinlord.antarchy.content.entity.multipart.MultipartEntityOwner; import com.craisinlord.antarchy.content.entity.multipart.network.MultipartAttackPayload; import com.craisinlord.antarchy.content.entity.multipart.network.MultipartInteractPayload; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; import com.craisinlord.antarchy.content.item.BrutalflyElytraFlightHelper; import com.craisinlord.antarchy.content.item.BrutalflyElytraItem; +import com.craisinlord.antarchy.content.item.BigBerthaItem; import com.craisinlord.antarchy.content.item.JumpyBootsHelper; import com.craisinlord.antarchy.fabric.util.JumpyBootsFabricHelper; import com.craisinlord.antarchy.content.item.JumpyBootsItem; @@ -47,6 +50,7 @@ public final class AntarchyFabricNetworking { PayloadTypeRegistry.playS2C().register(ScorpionWhipTetherPayload.TYPE, ScorpionWhipTetherPayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(BrutalflyElytraAnimationPayload.TYPE, BrutalflyElytraAnimationPayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(ThoraxisWeatherPayload.TYPE, ThoraxisWeatherPayload.STREAM_CODEC); + PayloadTypeRegistry.playS2C().register(HerculesBeetleImpactShakePayload.TYPE, HerculesBeetleImpactShakePayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(GravityGunPrimaryPayload.TYPE, GravityGunPrimaryPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(GravityGunScrollPayload.TYPE, GravityGunScrollPayload.STREAM_CODEC); @@ -55,6 +59,9 @@ public final class AntarchyFabricNetworking { PayloadTypeRegistry.playC2S().register(JumpyBootsLaunchPayload.TYPE, JumpyBootsLaunchPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(DorrieJumpInputPayload.TYPE, DorrieJumpInputPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(com.craisinlord.antarchy.content.network.DorrieChargeJumpPayload.TYPE, com.craisinlord.antarchy.content.network.DorrieChargeJumpPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(HerculesBeetleJumpInputPayload.TYPE, HerculesBeetleJumpInputPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(HerculesBeetleMountedAttackPayload.TYPE, HerculesBeetleMountedAttackPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(HerculesBeetleMountedChargePayload.TYPE, HerculesBeetleMountedChargePayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(MultipartAttackPayload.TYPE, MultipartAttackPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(MultipartInteractPayload.TYPE, MultipartInteractPayload.STREAM_CODEC); } @@ -74,6 +81,12 @@ public final class AntarchyFabricNetworking { context.server().execute(() -> handleDorrieJumpInput(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(com.craisinlord.antarchy.content.network.DorrieChargeJumpPayload.TYPE, (payload, context) -> context.server().execute(() -> handleDorrieChargeJumpInput(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(HerculesBeetleJumpInputPayload.TYPE, (payload, context) -> + context.server().execute(() -> handleHerculesBeetleJumpInput(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(HerculesBeetleMountedAttackPayload.TYPE, (payload, context) -> + context.server().execute(() -> handleHerculesBeetleMountedAttack(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(HerculesBeetleMountedChargePayload.TYPE, (payload, context) -> + context.server().execute(() -> handleHerculesBeetleMountedCharge(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(MultipartAttackPayload.TYPE, (payload, context) -> context.server().execute(() -> handleMultipartAttack(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(MultipartInteractPayload.TYPE, (payload, context) -> @@ -328,4 +341,26 @@ public final class AntarchyFabricNetworking { dorrie.releaseJump(); } } + + private static void handleHerculesBeetleJumpInput(ServerPlayer player, HerculesBeetleJumpInputPayload payload) { + if (player.getVehicle() instanceof HerculesBeetleEntity beetle) { + beetle.setRiderJumpPressed(payload.pressing()); + } + } + + private static void handleHerculesBeetleMountedAttack(ServerPlayer player, HerculesBeetleMountedAttackPayload payload) { + if (player.getVehicle() instanceof HerculesBeetleEntity beetle) { + beetle.handleMountedRegularAttack(player); + } + } + + private static void handleHerculesBeetleMountedCharge(ServerPlayer player, HerculesBeetleMountedChargePayload payload) { + if (player.getVehicle() instanceof HerculesBeetleEntity beetle) { + beetle.handleMountedCharge(player, payload.chargeTicks()); + } + } + + public static void triggerHerculesBeetleImpactShake(int durationTicks) { + HerculesBeetleImpactShakeClientState.trigger(durationTicks); + } } diff --git a/fabric/src/main/resources/antarchy-fabric.mixins.json b/fabric/src/main/resources/antarchy-fabric.mixins.json index ee3afe81..24133392 100644 --- a/fabric/src/main/resources/antarchy-fabric.mixins.json +++ b/fabric/src/main/resources/antarchy-fabric.mixins.json @@ -20,7 +20,7 @@ ], "client": [ "client.NightmareCameraShakeMixin", - "client.ToreterrorCameraShakeMixin", + "client.CameraShakeMixin", "client.DuctTapeInputMixin", "client.AntiwaterFogMixin", "client.GravityGunMouseScrollMixin", diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java index f3e7745e..8302837d 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java @@ -1,6 +1,9 @@ package com.craisinlord.antarchy.neoforge; import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.network.HerculesBeetleImpactShakePayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleImpactShakeSync; +import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.fml.ModContainer; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.event.config.ModConfigEvent; @@ -12,6 +15,7 @@ public final class AntarchyConfigModuleNeoforge { modContainer.registerConfig(ModConfig.Type.COMMON, AntarchyMobsConfig.SPEC, "antarchy/antarchy_mobs.toml"); modContainer.registerConfig(ModConfig.Type.COMMON, AntarchyToolsConfig.SPEC, "antarchy/antarchy_tools.toml"); modContainer.registerConfig(ModConfig.Type.COMMON, AntarchyMiscConfig.SPEC, "antarchy/antarchy_misc.toml"); + HerculesBeetleImpactShakeSync.setSink((player, ticks) -> PacketDistributor.sendToPlayer(player, new HerculesBeetleImpactShakePayload(ticks))); AntarchyNeoforge.modEventBusTempHolder.addListener(AntarchyConfigModuleNeoforge::onConfigChange); } @@ -169,6 +173,9 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setCheepHealth(AntarchyMobsConfig.cheepHealth()); AntarchySettings.setCheepAttackDamage(AntarchyMobsConfig.cheepAttackDamage()); AntarchySettings.setDorrieHealth(AntarchyMobsConfig.dorrieHealth()); + AntarchySettings.setHerculesBeetleHealth(AntarchyMobsConfig.herculesBeetleHealth()); + AntarchySettings.setHerculesBeetleAttackDamage(AntarchyMobsConfig.herculesBeetleAttackDamage()); + AntarchySettings.setHerculesBeetleChargeDamage(AntarchyMobsConfig.herculesBeetleChargeDamage()); } private static void bakeToolsConfig() { diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java index 1b3b7142..f2595747 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java @@ -193,6 +193,13 @@ public final class AntarchyMobsConfig { private static final ModConfigSpec.DoubleValue DORRIE_HEALTH; + // Hercules Beetle + + private static final ModConfigSpec.DoubleValue HERCULES_BEETLE_HEALTH; + private static final ModConfigSpec.DoubleValue HERCULES_BEETLE_ATTACK_DAMAGE; + private static final ModConfigSpec.DoubleValue HERCULES_BEETLE_CHARGE_DAMAGE; + + // Lucid private static final ModConfigSpec.DoubleValue LUCID_ATTACK_RANGE; @@ -493,6 +500,12 @@ public final class AntarchyMobsConfig { DORRIE_HEALTH = b.comment("Base max health.").defineInRange("health", 60.0D, 1.0D, 32768.0D); b.pop(); + b.push("herculesBeetle"); + HERCULES_BEETLE_HEALTH = b.comment("Base max health.").defineInRange("health", 500.0D, 1.0D, 32768.0D); + HERCULES_BEETLE_ATTACK_DAMAGE = b.comment("Base melee damage for ground and flying attacks.").defineInRange("attackDamage", 30.0D, 0.0D, 1024.0D); + HERCULES_BEETLE_CHARGE_DAMAGE = b.comment("AOE impact damage dealt when a charge collides.").defineInRange("chargeDamage", 40.0D, 0.0D, 1024.0D); + b.pop(); + b.push("toreterror"); TORETERROR_HEALTH = b.comment("Base max health.").defineInRange("health", 300.0D, 1.0D, 32768.0D); TORETERROR_JUMP_ATTACK_DAMAGE = b.comment("Damage dealt by the jump shockwave.").defineInRange("jumpAttackDamage", 14.0D, 0.0D, 1024.0D); @@ -652,4 +665,7 @@ public final class AntarchyMobsConfig { static double cheepHealth() { return CHEEP_HEALTH.get(); } static double cheepAttackDamage() { return CHEEP_ATTACK_DAMAGE.get(); } static double dorrieHealth() { return DORRIE_HEALTH.get(); } + static double herculesBeetleHealth() { return HERCULES_BEETLE_HEALTH.get(); } + static double herculesBeetleAttackDamage() { return HERCULES_BEETLE_ATTACK_DAMAGE.get(); } + static double herculesBeetleChargeDamage() { return HERCULES_BEETLE_CHARGE_DAMAGE.get(); } } diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java index 2f4c0970..4bfac523 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java @@ -268,6 +268,7 @@ public class AntarchyNeoforge { () -> AntarchyNeoforgeMisc.BLOODGLASS_MAX_HEARTS, () -> AntarchyNeoforgeMisc.BLOODGLASS_WARD ); + AntarchyObjects.setHerculesBeetle(AntarchyNeoforgeEntites.HERCULES_BEETLE); AntarchyObjects.setOctopusBomb(AntarchyNeoforgeEntites.OCTOPUS_BOMB); } diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java index 013be532..a73ff650 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java @@ -29,11 +29,20 @@ public final class AntarchyKeyBindings { CATEGORY ); + public static final KeyMapping HERCULES_BEETLE_CHARGE = new KeyMapping( + "key.antarchy.hercules_beetle_charge", + KeyConflictContext.IN_GAME, + InputConstants.Type.KEYSYM, + InputConstants.KEY_LCONTROL, + CATEGORY + ); + private AntarchyKeyBindings() {} @SubscribeEvent public static void onRegisterKeyMappings(RegisterKeyMappingsEvent event) { event.register(BRUTALFLY_FLAP); event.register(DORRIE_CHARGE_JUMP); + event.register(HERCULES_BEETLE_CHARGE); } } diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java index 31afd393..2cbcdc08 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java @@ -132,6 +132,7 @@ public final class AntarchyNeoForgeClient { event.registerEntityRenderer(AntarchyNeoforgeEntites.LURKING_TERROR.get(), LurkingTerrorRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.CHEEP.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.CheepRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.DORRIE.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.DorrieRenderer(context))); + event.registerEntityRenderer(AntarchyNeoforgeEntites.HERCULES_BEETLE.get(), context -> withParalyzedGeoLayer(new com.craisinlord.antarchy.content.client.renderer.HerculesBeetleRenderer(context))); } @SubscribeEvent diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/ToreterrorCameraShakeHandler.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/CameraShakeHandler.java similarity index 64% rename from neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/ToreterrorCameraShakeHandler.java rename to neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/CameraShakeHandler.java index 9fc1b0b3..75eb6c87 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/ToreterrorCameraShakeHandler.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/CameraShakeHandler.java @@ -1,8 +1,9 @@ package com.craisinlord.antarchy.neoforge.client; import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; import com.craisinlord.antarchy.content.entity.ToreterrorEntity; -import com.craisinlord.antarchy.content.item.BigBerthaItem; +import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity; import net.minecraft.client.Minecraft; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; @@ -12,11 +13,12 @@ import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.ViewportEvent; @EventBusSubscriber(modid = Antarchy.MODID, value = Dist.CLIENT) -public final class ToreterrorCameraShakeHandler { +public final class CameraShakeHandler { private static final double MAX_SHAKE_RANGE = 48.0D; private static final int TORETERROR_SHAKE_TICKS = 25; + private static final double NIGHTMARE_SHAKE_RANGE = 32.0D; - private ToreterrorCameraShakeHandler() { + private CameraShakeHandler() { } @SubscribeEvent @@ -37,8 +39,19 @@ public final class ToreterrorCameraShakeHandler { shakeStrength += (float) ((1.0D - distance / MAX_SHAKE_RANGE) * 1.0D); } - if (BigBerthaItem.clientShakeTicks > 0) { - shakeStrength += (float) BigBerthaItem.clientShakeTicks / TORETERROR_SHAKE_TICKS * 2.0F; + for (NightmareEntity nightmare : mc.level.getEntitiesOfClass( + NightmareEntity.class, + mc.player.getBoundingBox().inflate(NIGHTMARE_SHAKE_RANGE), + entity -> entity.isAlive() && entity.isRoaring() + )) { + double distance = Math.sqrt(cameraPos.distanceToSqr(nightmare.position().add(0.0D, nightmare.getBbHeight() * 0.5D, 0.0D))); + if (distance > NIGHTMARE_SHAKE_RANGE) continue; + shakeStrength += (float) ((1.0D - distance / NIGHTMARE_SHAKE_RANGE) * 1.35D); + } + + int beetleShakeTicks = HerculesBeetleImpactShakeClientState.getTicks(); + if (beetleShakeTicks > 0) { + shakeStrength += (float) beetleShakeTicks / TORETERROR_SHAKE_TICKS * 2.0F; } if (shakeStrength <= 0.0F) return; diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/HerculesBeetleClientHandler.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/HerculesBeetleClientHandler.java new file mode 100644 index 00000000..99d9b773 --- /dev/null +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/client/HerculesBeetleClientHandler.java @@ -0,0 +1,76 @@ +package com.craisinlord.antarchy.neoforge.client; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import com.craisinlord.antarchy.content.network.HerculesBeetleJumpInputPayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleMountedAttackPayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleMountedChargePayload; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.neoforge.network.PacketDistributor; + +@EventBusSubscriber(modid = Antarchy.MODID, value = Dist.CLIENT) +public final class HerculesBeetleClientHandler { + private static final int MAX_CHARGE_TICKS = 40; + + private static boolean wasPressingJump; + private static boolean wasPressingAttack; + private static boolean wasCharging; + private static int chargeTicks; + + private HerculesBeetleClientHandler() { + } + + @SubscribeEvent + public static void onClientTick(ClientTickEvent.Post event) { + HerculesBeetleImpactShakeClientState.tick(); + + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + if (player == null || mc.screen != null) { + resetAll(); + return; + } + + if (!(player.getVehicle() instanceof HerculesBeetleEntity)) { + resetAll(); + return; + } + + boolean pressingJump = mc.options.keyJump.isDown(); + if (pressingJump != wasPressingJump) { + PacketDistributor.sendToServer(new HerculesBeetleJumpInputPayload(pressingJump)); + wasPressingJump = pressingJump; + } + + boolean pressingAttack = mc.options.keyAttack.isDown(); + if (pressingAttack && !wasPressingAttack) { + PacketDistributor.sendToServer(new HerculesBeetleMountedAttackPayload()); + } + wasPressingAttack = pressingAttack; + + boolean charging = AntarchyKeyBindings.HERCULES_BEETLE_CHARGE.isDown(); + if (charging) { + chargeTicks = Math.min(chargeTicks + 1, MAX_CHARGE_TICKS); + } else if (wasCharging && chargeTicks > 0) { + PacketDistributor.sendToServer(new HerculesBeetleMountedChargePayload(chargeTicks)); + chargeTicks = 0; + } + wasCharging = charging; + } + + private static void resetAll() { + if (wasPressingJump) { + PacketDistributor.sendToServer(new HerculesBeetleJumpInputPayload(false)); + wasPressingJump = false; + } + wasPressingAttack = false; + wasCharging = false; + chargeTicks = 0; + } +} diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/network/HerculesBeetleNetworking.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/network/HerculesBeetleNetworking.java new file mode 100644 index 00000000..867cca1e --- /dev/null +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/network/HerculesBeetleNetworking.java @@ -0,0 +1,73 @@ +package com.craisinlord.antarchy.neoforge.network; + +import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; +import com.craisinlord.antarchy.content.network.HerculesBeetleImpactShakePayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleJumpInputPayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleMountedAttackPayload; +import com.craisinlord.antarchy.content.network.HerculesBeetleMountedChargePayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; + +public final class HerculesBeetleNetworking { + private HerculesBeetleNetworking() { + } + + public static void register(PayloadRegistrar registrar) { + registrar.playToServer( + HerculesBeetleJumpInputPayload.TYPE, + HerculesBeetleJumpInputPayload.STREAM_CODEC, + HerculesBeetleNetworking::handleJumpInput + ).playToServer( + HerculesBeetleMountedAttackPayload.TYPE, + HerculesBeetleMountedAttackPayload.STREAM_CODEC, + HerculesBeetleNetworking::handleMountedAttack + ).playToServer( + HerculesBeetleMountedChargePayload.TYPE, + HerculesBeetleMountedChargePayload.STREAM_CODEC, + HerculesBeetleNetworking::handleMountedCharge + ).playToClient( + HerculesBeetleImpactShakePayload.TYPE, + HerculesBeetleImpactShakePayload.STREAM_CODEC, + HerculesBeetleNetworking::handleImpactShake + ); + } + + private static void handleJumpInput(HerculesBeetleJumpInputPayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + if (!(context.player() instanceof ServerPlayer player)) { + return; + } + if (player.getVehicle() instanceof HerculesBeetleEntity beetle) { + beetle.setRiderJumpPressed(payload.pressing()); + } + }); + } + + private static void handleMountedAttack(HerculesBeetleMountedAttackPayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + if (!(context.player() instanceof ServerPlayer player)) { + return; + } + if (player.getVehicle() instanceof HerculesBeetleEntity beetle) { + beetle.handleMountedRegularAttack(player); + } + }); + } + + private static void handleMountedCharge(HerculesBeetleMountedChargePayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + if (!(context.player() instanceof ServerPlayer player)) { + return; + } + if (player.getVehicle() instanceof HerculesBeetleEntity beetle) { + beetle.handleMountedCharge(player, payload.chargeTicks()); + } + }); + } + + private static void handleImpactShake(HerculesBeetleImpactShakePayload payload, IPayloadContext context) { + context.enqueueWork(() -> HerculesBeetleImpactShakeClientState.trigger(payload.durationTicks())); + } +} diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeCreativeModeTabs.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeCreativeModeTabs.java index 14061079..829907a5 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeCreativeModeTabs.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeCreativeModeTabs.java @@ -115,7 +115,6 @@ public class AntarchyNeoforgeCreativeModeTabs { } if (event.getTabKey() == CreativeModeTabs.TOOLS_AND_UTILITIES) { - event.accept(AntarchyNeoforgeItems.CHEEP_ON_A_STICK.get()); event.insertAfter(new ItemStack(Items.SHEARS), new ItemStack(AntarchyNeoforgeItems.DUCT_TAPE_ITEM.get()), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); event.insertAfter(new ItemStack(Items.MINECART), new ItemStack(AntarchyNeoforgeItems.DIAMOND_MINECART_ITEM.get()), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); event.accept(AntarchyNeoforgeItems.BILE_BUCKET.get()); @@ -384,7 +383,7 @@ public class AntarchyNeoforgeCreativeModeTabs { // 8 - Blood crystal blocks (not the shard ingredient) case "blood_crystal_block", "small_blood_crystal_bud", "medium_blood_crystal_bud", "large_blood_crystal_bud", "budding_blood_crystal", "blood_crystal_cluster", - "myrmite", "broodstone", "chiten_block", "amber_moss_block", "amber_moss_carpet" -> 8; + "myrmite", "broodstone", "chiten_block", "chiten_spike", "amber_moss_block", "amber_moss_carpet" -> 8; // 9 - Ouranwood leaves case "ouranwood_leaves" -> 9; @@ -409,7 +408,7 @@ public class AntarchyNeoforgeCreativeModeTabs { // 15 - Tools case "ultimate_pickaxe", "ultimate_axe", "ultimate_shovel", "ultimate_hoe", - "duct_tape", "diamond_minecart", "gravity_sculk_sensor", "cheep_on_a_stick" -> 15; + "duct_tape", "diamond_minecart", "gravity_sculk_sensor" -> 15; // 16 - Liquid buckets case "bile_bucket", "ichor_bucket", "antiwater_bucket", "cloud_bucket" -> 16; diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java index 7cf43e25..cad5b817 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntites.java @@ -15,6 +15,7 @@ import com.craisinlord.antarchy.content.entity.DrTrayaurusEntity; import com.craisinlord.antarchy.content.entity.EasterBunnyEntity; import com.craisinlord.antarchy.content.entity.EmperorScorpionEntity; import com.craisinlord.antarchy.content.entity.HushProjectileEntity; +import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity; import com.craisinlord.antarchy.content.entity.JumpyBugEntity; import com.craisinlord.antarchy.content.entity.MantisEntity; import com.craisinlord.antarchy.content.entity.MissileSquidEntity; @@ -311,6 +312,11 @@ public final class AntarchyNeoforgeEntites { .sized(2.4F, 2.0F) .clientTrackingRange(10) .build("dorrie")); + public static final DeferredHolder, EntityType> HERCULES_BEETLE = ENTITY_TYPES.register("hercules_beetle", + () -> EntityType.Builder.of(HerculesBeetleEntity::new, MobCategory.MONSTER) + .sized(3.0F, 4.0F) + .clientTrackingRange(12) + .build("hercules_beetle")); public static final DeferredHolder, EntityType> UPWARD_FALLING_BLOCK = ENTITY_TYPES.register("upward_falling_block", () -> EntityType.Builder.of( UpwardFallingBlockEntity::new, MobCategory.MISC) diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntityAttributes.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntityAttributes.java index 7f8cb9ce..e83eccc3 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntityAttributes.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeEntityAttributes.java @@ -74,6 +74,7 @@ public class AntarchyNeoforgeEntityAttributes { event.put(AntarchyNeoforgeEntites.MOLEVORE.get(), MolevoreEntity.createAttributes().build()); event.put(AntarchyNeoforgeEntites.CHEEP.get(), com.craisinlord.antarchy.content.entity.CheepEntity.createAttributes().build()); event.put(AntarchyNeoforgeEntites.DORRIE.get(), com.craisinlord.antarchy.content.entity.DorrieEntity.createAttributes().build()); + event.put(AntarchyNeoforgeEntites.HERCULES_BEETLE.get(), com.craisinlord.antarchy.content.entity.HerculesBeetleEntity.createAttributes().build()); } private static AttributeSupplier.Builder buildAntAttributes(double health, double speed, double attackDamage) { diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java index 1375293a..4b20c614 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java @@ -7,7 +7,6 @@ import com.craisinlord.antarchy.content.block.entity.PotentNyxiteBlockEntity; import com.craisinlord.antarchy.content.block.entity.WaspNestBlockEntity; import com.craisinlord.antarchy.content.entity.SizeRayProjectileEntity; import com.craisinlord.antarchy.content.item.*; -import com.craisinlord.antarchy.content.item.CheepOnAStickItem; import com.craisinlord.antarchy.content.item.BloodCrystalArmorItem; import com.craisinlord.antarchy.content.item.BloodCrystalAppleItem; import com.craisinlord.antarchy.content.item.BloodCrystalKatanaItem; @@ -369,8 +368,6 @@ public final class AntarchyNeoforgeItems { .nutrition(2) .saturationModifier(0.2F) .build()))); - public static final DeferredItem CHEEP_ON_A_STICK = ITEMS.register("cheep_on_a_stick", - () -> new CheepOnAStickItem(new Item.Properties().stacksTo(1).durability(25))); public static final DeferredItem MOLEWORM_ITEM = ITEMS.register("moleworm", () -> new Item(new Item.Properties() .food(new FoodProperties.Builder() @@ -591,6 +588,8 @@ public final class AntarchyNeoforgeItems { () -> new DeferredSpawnEggItem(AntarchyNeoforgeEntites.CHEEP, 0xAA22FF, 0x22FF44, new Item.Properties())); public static final DeferredItem DORRIE_SPAWN_EGG = ITEMS.register("dorrie_spawn_egg", () -> new DeferredSpawnEggItem(AntarchyNeoforgeEntites.DORRIE, 0x1B91B3, 0xC0E8FF, new Item.Properties())); + public static final DeferredItem HERCULES_BEETLE_SPAWN_EGG = ITEMS.register("hercules_beetle_spawn_egg", + () -> new DeferredSpawnEggItem(AntarchyNeoforgeEntites.HERCULES_BEETLE, 0x6B1F2A, 0xD4AF37, new Item.Properties().rarity(Rarity.RARE))); private AntarchyNeoforgeItems() {} diff --git a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java index 64b32d56..c98e10cc 100644 --- a/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java +++ b/neoforge/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java @@ -4,6 +4,7 @@ import com.craisinlord.antarchy.content.network.BloodCrystalKatanaTrailPayload; import com.craisinlord.antarchy.neoforge.network.AntarchyGravityNetworking; import com.craisinlord.antarchy.neoforge.network.BrutalflyElytraNetworking; import com.craisinlord.antarchy.neoforge.network.DorrieJumpNetworking; +import com.craisinlord.antarchy.neoforge.network.HerculesBeetleNetworking; import com.craisinlord.antarchy.neoforge.network.JumpyBootsNetworking; import com.craisinlord.antarchy.neoforge.network.MultipartNetworking; import net.neoforged.bus.api.IEventBus; @@ -22,6 +23,7 @@ public class AntarchyNeoforgePayloadHandlers { BrutalflyElytraNetworking.register(registrar); JumpyBootsNetworking.register(registrar); DorrieJumpNetworking.register(registrar); + HerculesBeetleNetworking.register(registrar); registrar.playToClient( com.craisinlord.antarchy.content.network.BloodglassStatePayload.TYPE, com.craisinlord.antarchy.content.network.BloodglassStatePayload.STREAM_CODEC,