Hercules beetle assets, dorrie, cheep, start of toreterror sounds, etc
This commit is contained in:
1 parent
eb70e23b57
commit
cc87361ba9
98 files changed
+11154
-198
No files matched your search
@@ -273,8 +273,8 @@ public final class AntarchySettings {
|
||||
private static double bedBugAttackLungeHorizontalSpeed = 0.42D;
|
||||
private static double bedBugAttackLungeVerticalSpeed = 0.1D;
|
||||
private static float bedBugHealAmount = 2.0F;
|
||||
private static double jumpyBugHealth = 12.0D;
|
||||
private static double jumpyBugPounceDamage = 2.0D;
|
||||
private static double jumpyBugHealth = 100.0D;
|
||||
private static double jumpyBugPounceDamage = 12.0D;
|
||||
private static double jumpyBugLatchDamage = 1.0D;
|
||||
private static double jumpyBugCamouflageAlpha = 0.18D;
|
||||
private static double brutalflyArmor = 10.0D;
|
||||
@@ -467,6 +467,9 @@ public final class AntarchySettings {
|
||||
private static double creepingHorrorAttackDamage = 6.0D;
|
||||
private static double lurkingTerrorHealth = 15.0D;
|
||||
private static double lurkingTerrorAttackDamage = 6.0D;
|
||||
private static double cheepHealth = 8.0D;
|
||||
private static double cheepAttackDamage = 3.0D;
|
||||
private static double dorrieHealth = 60.0D;
|
||||
|
||||
private AntarchySettings() {
|
||||
}
|
||||
@@ -2390,4 +2393,10 @@ public final class AntarchySettings {
|
||||
public static void setLurkingTerrorHealth(double v) { lurkingTerrorHealth = v; }
|
||||
public static double lurkingTerrorAttackDamage() { return lurkingTerrorAttackDamage; }
|
||||
public static void setLurkingTerrorAttackDamage(double v) { lurkingTerrorAttackDamage = v; }
|
||||
public static double cheepHealth() { return cheepHealth; }
|
||||
public static void setCheepHealth(double v) { cheepHealth = v; }
|
||||
public static double cheepAttackDamage() { return cheepAttackDamage; }
|
||||
public static void setCheepAttackDamage(double v) { cheepAttackDamage = v; }
|
||||
public static double dorrieHealth() { return dorrieHealth; }
|
||||
public static void setDorrieHealth(double v) { dorrieHealth = v; }
|
||||
}
|
||||
@@ -92,6 +92,12 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<StinkBugEntity>> STINK_BUG = (Supplier<EntityType<StinkBugEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>> CHEEP = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.DorrieEntity>> DORRIE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.DorrieEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<net.minecraft.world.item.Item> CHEEP_ITEM = (Supplier<net.minecraft.world.item.Item>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> DUPLICATOR_LOG = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> DUPLICATOR_SAPLING = (Supplier<Block>) UNBOUND;
|
||||
@@ -254,6 +260,8 @@ public final class AntarchyObjects {
|
||||
Supplier<EntityType<CreepingHorrorEntity>> creepingHorror,
|
||||
Supplier<EntityType<LurkingTerrorEntity>> lurkingTerror,
|
||||
Supplier<EntityType<StinkBugEntity>> stinkBug,
|
||||
Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>> cheep,
|
||||
Supplier<EntityType<com.craisinlord.antarchy.content.entity.DorrieEntity>> dorrie,
|
||||
Supplier<Block> duplicatorLog,
|
||||
Supplier<Block> duplicatorSapling,
|
||||
Supplier<Block> ductTape,
|
||||
@@ -345,6 +353,8 @@ public final class AntarchyObjects {
|
||||
CREEPING_HORROR = creepingHorror;
|
||||
LURKING_TERROR = lurkingTerror;
|
||||
STINK_BUG = stinkBug;
|
||||
CHEEP = cheep;
|
||||
DORRIE = dorrie;
|
||||
DUPLICATOR_LOG = duplicatorLog;
|
||||
DUPLICATOR_SAPLING = duplicatorSapling;
|
||||
DUCT_TAPE = ductTape;
|
||||
|
||||
+29
-7
@@ -101,7 +101,7 @@ public class CreepingHorrorEggBlock extends Block {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random);
|
||||
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,13 +141,12 @@ public class CreepingHorrorEggBlock extends Block {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
||||
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
|
||||
if (level.random.nextFloat() >= PANIC_HATCH_CHANCE) continue;
|
||||
BlockState nearby = level.getBlockState(mutable);
|
||||
BlockPos immutable = mutable.immutable();
|
||||
if (nearby.getBlock() instanceof CreepingHorrorEggBlock b) {
|
||||
b.hatchEggs(level, immutable, nearby, level.random);
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
} else if (nearby.getBlock() instanceof LurkingTerrorEggBlock b) {
|
||||
b.hatchEggs(level, immutable, nearby, level.random);
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,11 +175,24 @@ public class CreepingHorrorEggBlock extends Block {
|
||||
return true;
|
||||
}
|
||||
|
||||
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
level.removeBlock(pos, false);
|
||||
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
|
||||
if (hatchedEggs <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random, hatchedEggs);
|
||||
}
|
||||
|
||||
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
|
||||
if (eggsToHatch >= state.getValue(EGGS)) {
|
||||
level.removeBlock(pos, false);
|
||||
} else {
|
||||
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
|
||||
}
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
|
||||
|
||||
for (int i = 0; i < state.getValue(EGGS); i++) {
|
||||
for (int i = 0; i < eggsToHatch; i++) {
|
||||
CreepingHorrorEntity entity = AntarchyObjects.CREEPING_HORROR.get().create(level);
|
||||
if (entity == null) {
|
||||
continue;
|
||||
@@ -192,6 +204,16 @@ public class CreepingHorrorEggBlock extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
private static int rollPanickedEggs(int eggs, RandomSource random) {
|
||||
int hatchedEggs = 0;
|
||||
for (int i = 0; i < eggs; i++) {
|
||||
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
|
||||
hatchedEggs++;
|
||||
}
|
||||
}
|
||||
return hatchedEggs;
|
||||
}
|
||||
|
||||
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
|
||||
int eggs = state.getValue(EGGS);
|
||||
if (eggs <= 1) {
|
||||
|
||||
+29
-7
@@ -101,7 +101,7 @@ public class LurkingTerrorEggBlock extends Block {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random);
|
||||
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,13 +141,12 @@ public class LurkingTerrorEggBlock extends Block {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
||||
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
|
||||
if (level.random.nextFloat() >= PANIC_HATCH_CHANCE) continue;
|
||||
BlockState nearby = level.getBlockState(mutable);
|
||||
BlockPos immutable = mutable.immutable();
|
||||
if (nearby.getBlock() instanceof LurkingTerrorEggBlock b) {
|
||||
b.hatchEggs(level, immutable, nearby, level.random);
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
} else if (nearby.getBlock() instanceof CreepingHorrorEggBlock b) {
|
||||
b.hatchEggs(level, immutable, nearby, level.random);
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,11 +175,24 @@ public class LurkingTerrorEggBlock extends Block {
|
||||
return true;
|
||||
}
|
||||
|
||||
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
level.removeBlock(pos, false);
|
||||
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
|
||||
if (hatchedEggs <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random, hatchedEggs);
|
||||
}
|
||||
|
||||
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
|
||||
if (eggsToHatch >= state.getValue(EGGS)) {
|
||||
level.removeBlock(pos, false);
|
||||
} else {
|
||||
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
|
||||
}
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
|
||||
|
||||
for (int i = 0; i < state.getValue(EGGS); i++) {
|
||||
for (int i = 0; i < eggsToHatch; i++) {
|
||||
LurkingTerrorEntity entity = AntarchyObjects.LURKING_TERROR.get().create(level);
|
||||
if (entity == null) {
|
||||
continue;
|
||||
@@ -192,6 +204,16 @@ public class LurkingTerrorEggBlock extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
private static int rollPanickedEggs(int eggs, RandomSource random) {
|
||||
int hatchedEggs = 0;
|
||||
for (int i = 0; i < eggs; i++) {
|
||||
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
|
||||
hatchedEggs++;
|
||||
}
|
||||
}
|
||||
return hatchedEggs;
|
||||
}
|
||||
|
||||
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
|
||||
int eggs = state.getValue(EGGS);
|
||||
if (eggs <= 1) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.CheepEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class CheepModel extends GeoModel<CheepEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(CheepEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/cheep.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(CheepEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/cheep/cheep_magenta.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(CheepEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/cheep.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.DorrieEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class DorrieModel extends GeoModel<DorrieEntity> {
|
||||
private static final ResourceLocation TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/dorrie.png");
|
||||
private static final ResourceLocation TEXTURE_SADDLED =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/dorrie_saddle.png");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(DorrieEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/dorrie.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(DorrieEntity animatable) {
|
||||
return animatable.hasSaddle() ? TEXTURE_SADDLED : TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(DorrieEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/dorrie.animation.json");
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.CheepModel;
|
||||
import com.craisinlord.antarchy.content.entity.CheepEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class CheepRenderer extends GeoEntityRenderer<CheepEntity> {
|
||||
public CheepRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new CheepModel());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.DorrieModel;
|
||||
import com.craisinlord.antarchy.content.entity.DorrieEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class DorrieRenderer extends GeoEntityRenderer<DorrieEntity> {
|
||||
public DorrieRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new DorrieModel());
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -17,7 +17,7 @@ import software.bernie.geckolib.util.Color;
|
||||
public class JumpyBugRenderer extends GeoEntityRenderer<JumpyBugEntity> {
|
||||
public JumpyBugRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new JumpyBugModel());
|
||||
this.shadowRadius = 3.0F;
|
||||
this.shadowRadius = 6.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -34,7 +34,7 @@ public class JumpyBugRenderer extends GeoEntityRenderer<JumpyBugEntity> {
|
||||
@Override
|
||||
public void preRender(PoseStack poseStack, JumpyBugEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
|
||||
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
|
||||
this.shadowRadius = animatable.isCamouflaged() ? 1.0F : 3.0F;
|
||||
this.shadowRadius = animatable.isCamouflaged() ? 1.0F : 6.0F;
|
||||
|
||||
if (animatable.isClingingToCeiling()) {
|
||||
poseStack.translate(0.0F, animatable.getBbHeight() + 0.05F, 0.0F);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
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<DorrieMenu> {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
private static final int DEFAULT_FUSE_TICKS = 120;
|
||||
private static final int EXPLODE_ANIM_START_TICKS = 25;
|
||||
private static final int FLASH_INTERVAL_TICKS = 5;
|
||||
private static final double KNOCKBACK_MULTIPLIER = 3.0D;
|
||||
private static final double KNOCKBACK_MULTIPLIER = 5.5D;
|
||||
private static final float EXPLOSION_RADIUS_MULTIPLIER = 0.45F;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
@@ -153,6 +153,7 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
this.primeFuse();
|
||||
}
|
||||
|
||||
this.playSound(AntarchySoundEvents.BOMBER_KNOCK.get(), 1.0F, 0.9F + this.random.nextFloat() * 0.2F);
|
||||
this.pushAwayFrom(source);
|
||||
return false;
|
||||
}
|
||||
@@ -259,7 +260,7 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
return PlayState.STOP;
|
||||
}
|
||||
|
||||
private void primeFuse() {
|
||||
public void primeFuse() {
|
||||
if (this.detonating) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.animal.AbstractFish;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class CheepEntity extends AbstractFish implements GeoEntity {
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim");
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenLoop("attack");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
public CheepEntity(EntityType<? extends CheepEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
super.registerGoals();
|
||||
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2D, true));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return AbstractFish.createAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.cheepHealth())
|
||||
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.cheepAttackDamage())
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.5D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getBucketItemStack() {
|
||||
return new ItemStack(Items.WATER_BUCKET);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.COD_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.COD_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource source) {
|
||||
return SoundEvents.COD_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getFlopSound() {
|
||||
return SoundEvents.COD_FLOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 2, this::mainAnimController));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<CheepEntity> state) {
|
||||
if (this.isAggressive()) {
|
||||
return state.setAndContinue(ATTACK_ANIM);
|
||||
}
|
||||
if (state.isMoving()) {
|
||||
return state.setAndContinue(SWIM_ANIM);
|
||||
}
|
||||
return state.setAndContinue(IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
}
|
||||
+24
-3
@@ -11,6 +11,7 @@ import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
@@ -27,6 +28,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
@@ -169,9 +171,28 @@ public class CreepingHorrorEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (!this.level().isClientSide()) {
|
||||
this.entityData.set(CLIMBING, this.horizontalCollision);
|
||||
}
|
||||
if (attackAnimTicks > 0) attackAnimTicks--;
|
||||
if (this.level().isClientSide()) return;
|
||||
|
||||
boolean climbing = this.horizontalCollision;
|
||||
this.entityData.set(CLIMBING, climbing);
|
||||
|
||||
if (climbing && this.getTarget() != null) {
|
||||
LivingEntity target = this.getTarget();
|
||||
Vec3 toTarget = target.position().subtract(this.position());
|
||||
double hDist = Math.sqrt(toTarget.x * toTarget.x + toTarget.z * toTarget.z);
|
||||
double speed = this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 2.8D;
|
||||
if (hDist > 0.01D) {
|
||||
this.setDeltaMovement(
|
||||
(toTarget.x / hDist) * speed,
|
||||
0.22D,
|
||||
(toTarget.z / hDist) * speed
|
||||
);
|
||||
} else {
|
||||
Vec3 mov = this.getDeltaMovement();
|
||||
this.setDeltaMovement(mov.x, 0.22D, mov.z);
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
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 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.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.AgeableMob;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Entity.MoveFunction;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.control.SmoothSwimmingLookControl;
|
||||
import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl;
|
||||
import net.minecraft.world.entity.ai.goal.BreathAirGoal;
|
||||
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.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;
|
||||
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;
|
||||
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 DorrieEntity extends Animal implements GeoEntity {
|
||||
private static final ResourceLocation CHEEP_ITEM_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "cheep");
|
||||
private static final EntityDataAccessor<Boolean> HAS_SADDLE =
|
||||
SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Boolean> TAMED =
|
||||
SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Integer> JUMP_CHARGE =
|
||||
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 RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim");
|
||||
private static final RawAnimation JUMP_START_ANIM = RawAnimation.begin().thenPlay("jump_1");
|
||||
private static final RawAnimation JUMP_LOOP_ANIM = RawAnimation.begin().thenLoop("jump_2");
|
||||
private static final RawAnimation JUMP_LAND_ANIM = RawAnimation.begin().thenPlay("jump_3");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
private int chargeTicks = 0;
|
||||
private boolean isCharging = false;
|
||||
private boolean isLeaping = false;
|
||||
private boolean wasInAir = false;
|
||||
private int landAnimTicks = 0;
|
||||
private boolean isPressingJump = false;
|
||||
|
||||
public DorrieEntity(EntityType<? extends DorrieEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, BASE_SPEED, 1.0F, true);
|
||||
this.lookControl = new SmoothSwimmingLookControl(this, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(HAS_SADDLE, false);
|
||||
builder.define(TAMED, false);
|
||||
builder.define(JUMP_CHARGE, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
return new WaterBoundPathNavigation(this, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new BreathAirGoal(this));
|
||||
this.goalSelector.addGoal(2, new TryFindWaterGoal(this));
|
||||
this.goalSelector.addGoal(3, new HuntCheepGoal(this));
|
||||
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
|
||||
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
||||
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.dorrieHealth())
|
||||
.add(Attributes.MOVEMENT_SPEED, BASE_SPEED)
|
||||
.add(Attributes.FOLLOW_RANGE, 16.0D)
|
||||
.add(Attributes.ATTACK_DAMAGE, 4.0D);
|
||||
}
|
||||
|
||||
|
||||
public boolean hasSaddle() {
|
||||
return this.entityData.get(HAS_SADDLE);
|
||||
}
|
||||
|
||||
public void setSaddle(boolean saddle) {
|
||||
this.entityData.set(HAS_SADDLE, saddle);
|
||||
}
|
||||
|
||||
public boolean isTamed() {
|
||||
return this.entityData.get(TAMED);
|
||||
}
|
||||
|
||||
public void setTamed(boolean tamed) {
|
||||
this.entityData.set(TAMED, tamed);
|
||||
}
|
||||
|
||||
public int getJumpCharge() {
|
||||
return this.entityData.get(JUMP_CHARGE);
|
||||
}
|
||||
|
||||
private void setJumpCharge(int charge) {
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.setTamed(tag.getBoolean("Tamed"));
|
||||
this.setSaddle(tag.getBoolean("HasSaddle"));
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
}
|
||||
return super.mobInteract(player, hand);
|
||||
}
|
||||
|
||||
// Remove saddle with empty hand
|
||||
if (this.hasSaddle() && stack.isEmpty() && !player.isPassengerOfSameVehicle(this)) {
|
||||
if (!this.level().isClientSide) {
|
||||
this.setSaddle(false);
|
||||
this.spawnAtLocation(new ItemStack(Items.SADDLE));
|
||||
this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 1.0F);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
Entity passenger = this.getFirstPassenger();
|
||||
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 strafe = player.xxa * 0.5F;
|
||||
return new Vec3(strafe, travelVector.y, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (landAnimTicks > 0) landAnimTicks--;
|
||||
|
||||
boolean inAirNow = !this.onGround() && !this.isInWater() && !this.isInLava();
|
||||
if (wasInAir && !inAirNow && isLeaping) {
|
||||
isLeaping = false;
|
||||
landAnimTicks = 12;
|
||||
}
|
||||
wasInAir = inAirNow;
|
||||
}
|
||||
|
||||
|
||||
/** 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();
|
||||
this.setDeltaMovement(
|
||||
look.x * horizontal * 0.7D + current.x * 0.3D,
|
||||
vertical,
|
||||
look.z * horizontal * 0.7D + current.z * 0.3D
|
||||
);
|
||||
this.hasImpulse = true;
|
||||
isLeaping = true;
|
||||
}
|
||||
isCharging = false;
|
||||
chargeTicks = 0;
|
||||
setJumpCharge(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void positionRider(Entity passenger, MoveFunction moveFunction) {
|
||||
if (this.hasPassenger(passenger)) {
|
||||
moveFunction.accept(passenger,
|
||||
this.getX(),
|
||||
this.getY() + this.getPassengersRidingOffset() + passenger.getBbHeight() * 0.5D,
|
||||
this.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
public double getPassengersRidingOffset() {
|
||||
return 0.6D;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void dropEquipment() {
|
||||
super.dropEquipment();
|
||||
if (this.hasSaddle()) {
|
||||
this.spawnAtLocation(new ItemStack(Items.SADDLE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DorrieEntity getBreedOffspring(ServerLevel level, AgeableMob mob) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.DOLPHIN_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource source) {
|
||||
return SoundEvents.DOLPHIN_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.DOLPHIN_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getSwimSound() {
|
||||
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<DorrieEntity> state) {
|
||||
if (landAnimTicks > 0) return state.setAndContinue(JUMP_LAND_ANIM);
|
||||
if (isLeaping) {
|
||||
return state.setAndContinue(wasInAir ? JUMP_LOOP_ANIM : JUMP_START_ANIM);
|
||||
}
|
||||
return state.setAndContinue(state.isMoving() ? SWIM_ANIM : IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
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 double HUNT_RANGE = 16.0D;
|
||||
private static final double ATTACK_RANGE_SQ = 2.5D * 2.5D;
|
||||
|
||||
private final DorrieEntity dorrie;
|
||||
private CheepEntity target;
|
||||
private int cooldown = 0;
|
||||
|
||||
HuntCheepGoal(DorrieEntity dorrie) {
|
||||
this.dorrie = dorrie;
|
||||
setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (dorrie.level().isClientSide) return false;
|
||||
if (cooldown > 0) { cooldown--; return false; }
|
||||
List<CheepEntity> nearby = dorrie.level().getEntitiesOfClass(
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return target != null && target.isAlive() && dorrie.distanceToSqr(target) <= HUNT_RANGE * HUNT_RANGE * 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
dorrie.getNavigation().moveTo(target, 1.4D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
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));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
target = null;
|
||||
dorrie.getNavigation().stop();
|
||||
cooldown = COOLDOWN_TICKS;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -64,17 +64,23 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
private static final String POUNCE_COOLDOWN_KEY = "PounceCooldown";
|
||||
private static final String RECOVERY_TICKS_KEY = "RecoveryTicks";
|
||||
private static final String POUNCE_TICKS_KEY = "PounceTicks";
|
||||
private static final String LEAPING_TICKS_KEY = "LeapingTicks";
|
||||
|
||||
private static final double DEFAULT_MOVEMENT_SPEED = 0.25D;
|
||||
private static final double DEFAULT_FOLLOW_RANGE = 16.0D;
|
||||
private static final double MELEE_WALK_RANGE = 4.0D;
|
||||
private static final double CHASE_LEAP_RANGE_SQR = 8.0D * 8.0D;
|
||||
private static final int DEFAULT_POUNCE_COOLDOWN = 60;
|
||||
private static final int DEFAULT_RECOVERY_TICKS = 20;
|
||||
private static final int CHASE_JUMP_COOLDOWN = 25;
|
||||
private static final double POUNCE_HORIZONTAL_SPEED = 0.92D;
|
||||
private static final double POUNCE_UPWARD_SPEED = 0.75D;
|
||||
private static final double CHASE_LEAP_HORIZONTAL = 1.5D;
|
||||
private static final double CHASE_LEAP_VERTICAL = 0.55D;
|
||||
private static final double QUICK_MOVE_SQR = 0.14D * 0.14D;
|
||||
private static final double CLING_SEARCH_RANGE = 1.5D;
|
||||
private static final double POUNCE_LAND_DAMAGE_RANGE = 5.0D;
|
||||
private static final int CEILING_SEARCH_RANGE = 30;
|
||||
private static final double CAMOUFLAGE_PLAYER_RANGE = 24.0D;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
@@ -86,6 +92,8 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
private int pounceCooldownTicks;
|
||||
private int recoveryTicks;
|
||||
private int pounceTicks;
|
||||
private int leapingTicks;
|
||||
private int chaseJumpCooldownTicks;
|
||||
private float visualAlpha = 1.0F;
|
||||
private float previousVisualAlpha = 1.0F;
|
||||
|
||||
@@ -122,12 +130,13 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new PounceAttackGoal());
|
||||
this.goalSelector.addGoal(2, new CeilingClingGoal());
|
||||
this.goalSelector.addGoal(3, new ChaseTargetGoal());
|
||||
this.goalSelector.addGoal(4, new WanderGoal());
|
||||
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
||||
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
||||
this.goalSelector.addGoal(1, new CloseMeleeGoal());
|
||||
this.goalSelector.addGoal(2, new PounceAttackGoal());
|
||||
this.goalSelector.addGoal(3, new HideOnCeilingGoal());
|
||||
this.goalSelector.addGoal(4, new ChaseTargetGoal());
|
||||
this.goalSelector.addGoal(5, new WanderGoal());
|
||||
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
||||
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
}
|
||||
@@ -148,6 +157,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
tag.putInt(POUNCE_COOLDOWN_KEY, this.pounceCooldownTicks);
|
||||
tag.putInt(RECOVERY_TICKS_KEY, this.recoveryTicks);
|
||||
tag.putInt(POUNCE_TICKS_KEY, this.pounceTicks);
|
||||
tag.putInt(LEAPING_TICKS_KEY, this.leapingTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,6 +166,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
this.pounceCooldownTicks = tag.getInt(POUNCE_COOLDOWN_KEY);
|
||||
this.recoveryTicks = tag.getInt(RECOVERY_TICKS_KEY);
|
||||
this.pounceTicks = tag.getInt(POUNCE_TICKS_KEY);
|
||||
this.leapingTicks = tag.getInt(LEAPING_TICKS_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,7 +179,10 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
public void tick() {
|
||||
this.previousVisualAlpha = this.visualAlpha;
|
||||
this.visualAlpha += (this.getTargetVisualAlpha() - this.visualAlpha) * 0.25F;
|
||||
float targetAlpha = this.getTargetVisualAlpha();
|
||||
if (this.visualAlpha != targetAlpha) {
|
||||
this.visualAlpha += (targetAlpha - this.visualAlpha) * 0.25F;
|
||||
}
|
||||
|
||||
super.tick();
|
||||
|
||||
@@ -182,11 +196,16 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
if (this.recoveryTicks > 0) {
|
||||
this.recoveryTicks--;
|
||||
}
|
||||
if (this.chaseJumpCooldownTicks > 0) {
|
||||
this.chaseJumpCooldownTicks--;
|
||||
}
|
||||
|
||||
if (this.isClingingToCeiling()) {
|
||||
this.tickClinging();
|
||||
} else if (this.isPouncing()) {
|
||||
this.tickPouncing();
|
||||
} else if (this.isLeapingToCeiling()) {
|
||||
this.tickLeapingToCeiling();
|
||||
} else {
|
||||
this.setNoGravity(false);
|
||||
this.noPhysics = false;
|
||||
@@ -222,6 +241,9 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
if (this.isPouncing()) {
|
||||
this.finishPounce(false);
|
||||
}
|
||||
if (this.isLeapingToCeiling()) {
|
||||
this.cancelLeap();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -262,6 +284,10 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
return this.getAttackState() == AttackState.POUNCING;
|
||||
}
|
||||
|
||||
public boolean isLeapingToCeiling() {
|
||||
return this.getAttackState() == AttackState.LEAPING_TO_CEILING;
|
||||
}
|
||||
|
||||
public float getVisualAlpha(float partialTick) {
|
||||
return Mth.lerp(partialTick, this.previousVisualAlpha, this.visualAlpha);
|
||||
}
|
||||
@@ -288,7 +314,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
|
||||
private PlayState mainAnimController(AnimationState<JumpyBugEntity> state) {
|
||||
AttackState attackState = this.getAttackState();
|
||||
if (attackState == AttackState.POUNCING || attackState == AttackState.CLINGING) {
|
||||
if (attackState == AttackState.POUNCING || attackState == AttackState.CLINGING || attackState == AttackState.LEAPING_TO_CEILING) {
|
||||
return state.setAndContinue(JUMP_ANIM);
|
||||
}
|
||||
if (attackState == AttackState.RECOVERING) {
|
||||
@@ -298,7 +324,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private void tickClinging() {
|
||||
BlockPos clingPos = this.findClingBlockPos();
|
||||
BlockPos clingPos = this.findCeilingWithinRange(2);
|
||||
if (clingPos == null) {
|
||||
this.stopClinging();
|
||||
return;
|
||||
@@ -328,37 +354,61 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private void tickLeapingToCeiling() {
|
||||
this.leapingTicks++;
|
||||
if (this.leapingTicks > 80) {
|
||||
this.cancelLeap();
|
||||
return;
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
BlockPos ceiling = this.findCeilingWithinRange(2);
|
||||
if (ceiling != null) {
|
||||
this.leapingTicks = 0;
|
||||
this.beginClinging();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCamouflage() {
|
||||
if (this.isPouncing()) {
|
||||
if (this.isPouncing() || this.isLeapingToCeiling()) {
|
||||
this.setCamouflaged(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isClingingToCeiling()) {
|
||||
this.setCamouflaged(true);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean playerNearby = this.level().getNearestPlayer(this, CAMOUFLAGE_PLAYER_RANGE) != null;
|
||||
if (!playerNearby) {
|
||||
this.setCamouflaged(false);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean idle = this.getTarget() == null && this.getDeltaMovement().horizontalDistanceSqr() < 0.003D;
|
||||
boolean stalking = this.isClingingToCeiling();
|
||||
boolean movingQuickly = this.getDeltaMovement().lengthSqr() > QUICK_MOVE_SQR;
|
||||
boolean shouldCamouflage = !movingQuickly && (idle || stalking);
|
||||
this.setCamouflaged(shouldCamouflage);
|
||||
this.setCamouflaged(!movingQuickly && idle);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos findClingBlockPos() {
|
||||
private BlockPos findCeilingWithinRange(int maxBlocks) {
|
||||
BlockPos origin = this.blockPosition();
|
||||
for (int dy = 1; dy <= 2; dy++) {
|
||||
for (int dy = 1; dy <= maxBlocks; dy++) {
|
||||
BlockPos candidate = origin.above(dy);
|
||||
if (this.isCeilingBlock(candidate)) {
|
||||
if (isCeilingBlock(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCeilingBlock(BlockPos pos) {
|
||||
return this.level().getBlockState(pos).isFaceSturdy(this.level(), pos, net.minecraft.core.Direction.DOWN);
|
||||
@Nullable
|
||||
private BlockPos findCeilingForLeap() {
|
||||
return findCeilingWithinRange(CEILING_SEARCH_RANGE);
|
||||
}
|
||||
|
||||
private boolean canClingHere() {
|
||||
return this.findClingBlockPos() != null;
|
||||
private boolean isCeilingBlock(BlockPos pos) {
|
||||
return this.level().getBlockState(pos).isFaceSturdy(this.level(), pos, net.minecraft.core.Direction.DOWN);
|
||||
}
|
||||
|
||||
private void beginClinging() {
|
||||
@@ -377,6 +427,23 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
this.noPhysics = false;
|
||||
}
|
||||
|
||||
private void beginLeapToCeiling(BlockPos ceilingPos) {
|
||||
this.setAttackState(AttackState.LEAPING_TO_CEILING);
|
||||
this.setCamouflaged(false);
|
||||
this.leapingTicks = 0;
|
||||
this.getNavigation().stop();
|
||||
|
||||
double dy = ceilingPos.getY() - this.getY();
|
||||
double upwardSpeed = Math.min(2.5D, Math.sqrt(0.16D * Math.max(dy, 1.0D)) + 0.4D);
|
||||
this.setDeltaMovement(0.0D, upwardSpeed, 0.0D);
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
|
||||
private void cancelLeap() {
|
||||
this.leapingTicks = 0;
|
||||
this.setAttackState(AttackState.GROUND);
|
||||
}
|
||||
|
||||
private void beginPounce(LivingEntity target) {
|
||||
this.stopClinging();
|
||||
this.setTarget(target);
|
||||
@@ -407,14 +474,16 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
|
||||
private boolean canUseAmbush() {
|
||||
return !this.isPouncing()
|
||||
&& !this.isLeapingToCeiling()
|
||||
&& this.recoveryTicks <= 0;
|
||||
}
|
||||
|
||||
private enum AttackState implements StringRepresentable {
|
||||
public enum AttackState implements StringRepresentable {
|
||||
GROUND(0, "ground"),
|
||||
CLINGING(1, "clinging"),
|
||||
POUNCING(2, "pouncing"),
|
||||
RECOVERING(3, "recovering");
|
||||
RECOVERING(3, "recovering"),
|
||||
LEAPING_TO_CEILING(4, "leaping_to_ceiling");
|
||||
|
||||
private static final IntFunction<AttackState> BY_ID = ByIdMap.continuous(AttackState::id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
|
||||
public static final Codec<AttackState> CODEC = StringRepresentable.fromEnum(AttackState::values);
|
||||
@@ -437,6 +506,61 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private final class CloseMeleeGoal extends Goal {
|
||||
private static final double MELEE_RANGE_SQR = 5.0D * 5.0D;
|
||||
private static final double CONTINUE_RANGE_SQR = 7.0D * 7.0D;
|
||||
private static final int ATTACK_COOLDOWN = 20;
|
||||
private int attackCooldown;
|
||||
|
||||
private CloseMeleeGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& JumpyBugEntity.this.distanceToSqr(target) <= MELEE_RANGE_SQR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& JumpyBugEntity.this.distanceToSqr(target) <= CONTINUE_RANGE_SQR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.attackCooldown = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target == null) return;
|
||||
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
JumpyBugEntity.this.getNavigation().moveTo(target, 1.1D);
|
||||
|
||||
if (this.attackCooldown > 0) {
|
||||
this.attackCooldown--;
|
||||
}
|
||||
if (this.attackCooldown <= 0 && JumpyBugEntity.this.distanceToSqr(target) <= MELEE_RANGE_SQR) {
|
||||
JumpyBugEntity.this.doHurtTarget(target);
|
||||
this.attackCooldown = ATTACK_COOLDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PounceAttackGoal extends Goal {
|
||||
private PounceAttackGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
|
||||
@@ -466,8 +590,11 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private final class CeilingClingGoal extends Goal {
|
||||
private CeilingClingGoal() {
|
||||
private final class HideOnCeilingGoal extends Goal {
|
||||
@Nullable
|
||||
private BlockPos targetCeiling;
|
||||
|
||||
private HideOnCeilingGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
|
||||
}
|
||||
|
||||
@@ -476,45 +603,60 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
if (!JumpyBugEntity.this.canUseAmbush() || JumpyBugEntity.this.isClingingToCeiling()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target == null) {
|
||||
return JumpyBugEntity.this.random.nextInt(40) == 0 && JumpyBugEntity.this.canClingHere();
|
||||
boolean targetFarAway = target != null
|
||||
&& JumpyBugEntity.this.distanceToSqr(target) > 12.0D * 12.0D;
|
||||
boolean noTarget = target == null;
|
||||
if (!noTarget && !targetFarAway) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JumpyBugEntity.this.distanceToSqr(target) <= (DEFAULT_FOLLOW_RANGE + CLING_SEARCH_RANGE) * (DEFAULT_FOLLOW_RANGE + CLING_SEARCH_RANGE)
|
||||
&& JumpyBugEntity.this.canClingHere();
|
||||
if (JumpyBugEntity.this.random.nextInt(25) != 0) {
|
||||
return false;
|
||||
}
|
||||
this.targetCeiling = JumpyBugEntity.this.findCeilingForLeap();
|
||||
return this.targetCeiling != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return JumpyBugEntity.this.isClingingToCeiling();
|
||||
return JumpyBugEntity.this.isClingingToCeiling() || JumpyBugEntity.this.isLeapingToCeiling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
JumpyBugEntity.this.beginClinging();
|
||||
if (this.targetCeiling != null) {
|
||||
BlockPos ceil = this.targetCeiling;
|
||||
// If we're already adjacent to the ceiling, just cling directly
|
||||
if (ceil.getY() - JumpyBugEntity.this.getY() <= 2) {
|
||||
JumpyBugEntity.this.beginClinging();
|
||||
} else {
|
||||
JumpyBugEntity.this.beginLeapToCeiling(ceil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (JumpyBugEntity.this.isClingingToCeiling()) {
|
||||
JumpyBugEntity.this.stopClinging();
|
||||
} else if (JumpyBugEntity.this.isLeapingToCeiling()) {
|
||||
JumpyBugEntity.this.cancelLeap();
|
||||
}
|
||||
this.targetCeiling = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
if (JumpyBugEntity.this.pounceCooldownTicks <= 0
|
||||
&& (JumpyBugEntity.this.hasLineOfSight(target) || JumpyBugEntity.this.distanceToSqr(target) <= 4.0D)) {
|
||||
JumpyBugEntity.this.stopClinging();
|
||||
JumpyBugEntity.this.beginPounce(target);
|
||||
if (JumpyBugEntity.this.isClingingToCeiling()) {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target != null) {
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
if (JumpyBugEntity.this.pounceCooldownTicks <= 0
|
||||
&& (JumpyBugEntity.this.hasLineOfSight(target) || JumpyBugEntity.this.distanceToSqr(target) <= 4.0D)) {
|
||||
JumpyBugEntity.this.stopClinging();
|
||||
JumpyBugEntity.this.beginPounce(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,6 +673,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
&& target.isAlive()
|
||||
&& !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& JumpyBugEntity.this.recoveryTicks <= 0;
|
||||
}
|
||||
|
||||
@@ -547,11 +690,24 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
// Only walk when close; at range, pounce is the primary approach
|
||||
if (JumpyBugEntity.this.distanceToSqr(target) <= MELEE_WALK_RANGE * MELEE_WALK_RANGE) {
|
||||
double distSqr = JumpyBugEntity.this.distanceToSqr(target);
|
||||
|
||||
if (distSqr <= MELEE_WALK_RANGE * MELEE_WALK_RANGE) {
|
||||
JumpyBugEntity.this.getNavigation().moveTo(target, 1.05D);
|
||||
} else {
|
||||
} else if (distSqr > CHASE_LEAP_RANGE_SQR) {
|
||||
if (JumpyBugEntity.this.onGround() && JumpyBugEntity.this.chaseJumpCooldownTicks <= 0) {
|
||||
Vec3 dir = target.position().subtract(JumpyBugEntity.this.position()).normalize();
|
||||
JumpyBugEntity.this.setDeltaMovement(
|
||||
dir.x * CHASE_LEAP_HORIZONTAL,
|
||||
CHASE_LEAP_VERTICAL,
|
||||
dir.z * CHASE_LEAP_HORIZONTAL
|
||||
);
|
||||
JumpyBugEntity.this.hasImpulse = true;
|
||||
JumpyBugEntity.this.chaseJumpCooldownTicks = CHASE_JUMP_COOLDOWN;
|
||||
}
|
||||
JumpyBugEntity.this.getNavigation().stop();
|
||||
} else {
|
||||
JumpyBugEntity.this.getNavigation().moveTo(target, 1.05D);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,6 +721,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
public boolean canUse() {
|
||||
return !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& JumpyBugEntity.this.getTarget() == null
|
||||
&& super.canUse();
|
||||
}
|
||||
@@ -573,6 +730,7 @@ public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
public boolean canContinueToUse() {
|
||||
return !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& super.canContinueToUse();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ public class LurkingTerrorEntity extends Monster implements GeoEntity {
|
||||
if (!this.level().isClientSide() && !this.onGround() && this.tickCount % 40 == 0) {
|
||||
this.playSound(AntarchySoundEvents.LURKING_TERROR_FLY_LOOP.get(), 0.45F, 0.95F + this.random.nextFloat() * 0.1F);
|
||||
}
|
||||
if (attackAnimTicks > 0) attackAnimTicks--;
|
||||
if (!this.level().isClientSide() && attackAnimTicks > 0) attackAnimTicks--;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,7 +52,7 @@ public class SpitBugEntity extends Monster implements GeoEntity {
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation SPIT_ANIM = RawAnimation.begin().thenPlay("spit");
|
||||
|
||||
private static final double MAX_HEALTH = 30.0D;
|
||||
private static final double MAX_HEALTH = 50.0D;
|
||||
private static final double MOVEMENT_SPEED = 0.23D;
|
||||
private static final double ATTACK_DAMAGE = 5.0D;
|
||||
private static final double FOLLOW_RANGE = 24.0D;
|
||||
|
||||
+38
-48
@@ -70,19 +70,17 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
private static final int ANIM_RAPID_SHOOT = 8;
|
||||
|
||||
private static final int ATTACK_RAPID_FIRE = 0;
|
||||
private static final int ATTACK_HUGE_BOMB = 1;
|
||||
private static final int ATTACK_BOMBERS = 2;
|
||||
private static final int ATTACK_BOMBERS = 1;
|
||||
|
||||
private static final int DEATH_TICKS = 60;
|
||||
private static final int SHOOT_TOTAL_TICKS = 75;
|
||||
private static final int SHOOT_FIRE_START_TICK = 12;
|
||||
private static final int RAPID_FIRE_INTERVAL = 4;
|
||||
private static final int RAPID_FIRE_END_TICK = 60;
|
||||
private static final int SHOOT_BOMBER_TICK = 12;
|
||||
private static final int SHOOT_BOMBER_SECOND_TICK = 32;
|
||||
private static final int JUMP_TOTAL_TICKS = 40;
|
||||
private static final int JUMP_TOTAL_TICKS = 55;
|
||||
private static final int JUMP_LAUNCH_TICK = 10;
|
||||
private static final int JUMP_SHAKE_TICKS = 25;
|
||||
private static final int JUMP_SHAKE_TICKS = 12;
|
||||
private static final int SPIN_START_TOTAL_TICKS = 5;
|
||||
private static final int SPIN_LOOP_MIN_TICKS = 200;
|
||||
private static final int SPIN_LOOP_MAX_TICKS = 300;
|
||||
@@ -96,8 +94,8 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
private static final double SPIN_TRIGGER_RANGE_SQR = 225.0D;
|
||||
private static final double SPIN_LEASH_RANGE_SQR = 225.0D;
|
||||
private static final double SPIN_RADIUS = 4.5D;
|
||||
private static final double SPIN_MOVE_SPEED = 1.7D;
|
||||
private static final double SPIN_ANIMATION_SPEED = 0.7D;
|
||||
private static final double SPIN_MOVE_SPEED = 0.833D;
|
||||
private static final double SPIN_ANIMATION_SPEED = 0.49D;
|
||||
private static final double JUMP_SHOCKWAVE_RADIUS = 6.0D;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("Idle");
|
||||
@@ -123,6 +121,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
private int jumpCooldown;
|
||||
private int spinCooldown;
|
||||
private boolean jumpLaunched;
|
||||
private boolean jumpAirborne;
|
||||
private boolean shockwaveApplied;
|
||||
private int jumpShakeTicks;
|
||||
private int shootAttackType;
|
||||
@@ -274,7 +273,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
return this.entityData.get(JUMP_SHAKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void startSeenByPlayer(ServerPlayer player) {
|
||||
super.startSeenByPlayer(player);
|
||||
this.bossEvent.addPlayer(player);
|
||||
@@ -377,12 +376,6 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
this.fireRapidBomb();
|
||||
}
|
||||
}
|
||||
case ATTACK_HUGE_BOMB -> {
|
||||
this.setAnimationState(ANIM_SHOOT);
|
||||
if (elapsed == SHOOT_FIRE_START_TICK) {
|
||||
this.fireHugeBomb();
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
this.setAnimationState(ANIM_SHOOT);
|
||||
if (!this.shootBomberFired && elapsed >= SHOOT_BOMBER_TICK) {
|
||||
@@ -398,7 +391,12 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.85D));
|
||||
if (this.getTarget() != null) {
|
||||
this.getLookControl().setLookAt(this.getTarget(), 30.0F, 20.0F);
|
||||
Vec3 toTarget = this.getTarget().position().subtract(this.position());
|
||||
float yaw = (float) (net.minecraft.util.Mth.atan2(toTarget.z, toTarget.x) * (180.0D / Math.PI)) - 90.0F;
|
||||
this.setYRot(yaw);
|
||||
this.yBodyRot = yaw;
|
||||
this.yHeadRot = yaw;
|
||||
this.setYHeadRot(yaw);
|
||||
}
|
||||
this.shootAnimTicks--;
|
||||
if (this.shootAnimTicks <= 0) {
|
||||
@@ -414,15 +412,29 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target != null) {
|
||||
Vec3 toTarget = target.position().subtract(this.position()).normalize();
|
||||
this.setDeltaMovement(toTarget.x * 0.6D, 0.85D, toTarget.z * 0.6D);
|
||||
this.setDeltaMovement(toTarget.x * 0.6D, 1.4D, toTarget.z * 0.6D);
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
}
|
||||
if (this.jumpLaunched && this.onGround() && elapsed > JUMP_LAUNCH_TICK + 3 && !this.shockwaveApplied) {
|
||||
this.shockwaveApplied = true;
|
||||
this.applyJumpShockwave();
|
||||
this.jumpShakeTicks = JUMP_SHAKE_TICKS;
|
||||
this.entityData.set(JUMP_SHAKING, true);
|
||||
if (this.jumpLaunched && !this.onGround()) {
|
||||
this.jumpAirborne = true;
|
||||
}
|
||||
if (this.jumpLaunched && this.jumpAirborne && !this.shockwaveApplied) {
|
||||
boolean forceLand = this.jumpAnimTicks <= 1;
|
||||
if (forceLand) {
|
||||
BlockPos pos = BlockPos.containing(this.getX(), this.getY(), this.getZ());
|
||||
while (pos.getY() > this.level().getMinBuildHeight() && this.level().isEmptyBlock(pos)) {
|
||||
pos = pos.below();
|
||||
}
|
||||
this.teleportTo(this.getX(), pos.getY() + 1.0, this.getZ());
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
if (this.onGround() || forceLand) {
|
||||
this.shockwaveApplied = true;
|
||||
this.applyJumpShockwave();
|
||||
this.jumpShakeTicks = JUMP_SHAKE_TICKS;
|
||||
this.entityData.set(JUMP_SHAKING, true);
|
||||
}
|
||||
}
|
||||
this.jumpAnimTicks--;
|
||||
if (this.jumpAnimTicks <= 0) {
|
||||
@@ -509,8 +521,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
Vec3 forward = this.spinDirection.lengthSqr() > 1.0E-4D ? this.spinDirection.normalize() : towardTarget;
|
||||
Vec3 strafe = new Vec3(-towardTarget.z, 0.0D, towardTarget.x).scale(this.spinStrafeSign);
|
||||
Vec3 combined = forward.scale(1.2D).add(towardTarget.scale(0.15D)).add(strafe.scale(1.35D));
|
||||
Vec3 combined = forward.scale(0.95D).add(towardTarget.scale(0.05D));
|
||||
this.spinDirection = combined.lengthSqr() > 1.0E-4D ? combined.normalize() : towardTarget;
|
||||
}
|
||||
|
||||
@@ -677,22 +688,6 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private void fireHugeBomb() {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) return;
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null) return;
|
||||
this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
Vec3 from = new Vec3(this.getX(), this.getEyeY(), this.getZ());
|
||||
Vec3 to = new Vec3(target.getX(), target.getY(), target.getZ());
|
||||
Vec3 vel = calcLaunchVelocity(from, to, 0.9);
|
||||
WaterBombEntity bomb = new WaterBombEntity(this.level(), this, true);
|
||||
bomb.setPos(from.x, from.y, from.z);
|
||||
bomb.setDeltaMovement(vel);
|
||||
bomb.hasImpulse = true;
|
||||
serverLevel.addFreshEntity(bomb);
|
||||
}
|
||||
|
||||
|
||||
private void fireBomberBurst() {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) return;
|
||||
LivingEntity target = this.getTarget();
|
||||
@@ -710,6 +705,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
bomber.setDeltaMovement(vx, vy, vz);
|
||||
bomber.setTarget(target);
|
||||
serverLevel.addFreshEntity(bomber);
|
||||
bomber.primeFuse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,15 +774,8 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
ToreterrorEntity.this.shootBomberFiredSecond = false;
|
||||
ToreterrorEntity.this.rapidFireNextTick = 0;
|
||||
float roll = ToreterrorEntity.this.random.nextFloat();
|
||||
if (ToreterrorEntity.this.isPhase2()) {
|
||||
if (roll < 0.5F) ToreterrorEntity.this.shootAttackType = ATTACK_RAPID_FIRE;
|
||||
else if (roll < 0.65F) ToreterrorEntity.this.shootAttackType = ATTACK_HUGE_BOMB;
|
||||
else ToreterrorEntity.this.shootAttackType = ATTACK_BOMBERS;
|
||||
} else {
|
||||
if (roll < 0.5F) ToreterrorEntity.this.shootAttackType = ATTACK_RAPID_FIRE;
|
||||
else if (roll < 0.7F) ToreterrorEntity.this.shootAttackType = ATTACK_HUGE_BOMB;
|
||||
else ToreterrorEntity.this.shootAttackType = ATTACK_BOMBERS;
|
||||
}
|
||||
if (roll < 0.5F) ToreterrorEntity.this.shootAttackType = ATTACK_RAPID_FIRE;
|
||||
else ToreterrorEntity.this.shootAttackType = ATTACK_BOMBERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -818,6 +807,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
public void start() {
|
||||
ToreterrorEntity.this.jumpAnimTicks = JUMP_TOTAL_TICKS;
|
||||
ToreterrorEntity.this.jumpLaunched = false;
|
||||
ToreterrorEntity.this.jumpAirborne = false;
|
||||
ToreterrorEntity.this.shockwaveApplied = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,9 +117,6 @@ public class WaterBombEntity extends ThrowableProjectile {
|
||||
for (BlockPos pos : BlockPos.betweenClosed(hitPos.offset(-1, 0, -1), hitPos.offset(1, 0, 1))) {
|
||||
if (level.getBlockState(pos).getBlock() instanceof BaseFireBlock) {
|
||||
level.removeBlock(pos, false);
|
||||
} else if (level.isEmptyBlock(pos)) {
|
||||
level.setBlock(pos, Blocks.WATER.defaultBlockState(), 3);
|
||||
level.scheduleTick(pos, Fluids.WATER, 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+40
@@ -5,6 +5,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.PlayerRespawnLogic;
|
||||
@@ -113,6 +114,11 @@ public final class AntTeleportHelper {
|
||||
|
||||
@Nullable
|
||||
private static Vec3 findSafeArrivalPosition(ServerPlayer player, ServerLevel destination, BlockPos preferredPos) {
|
||||
int[] yRange = getDimensionYRange(destination);
|
||||
if (yRange != null) {
|
||||
return findSafeArrivalPositionInYRange(player, destination, preferredPos, yRange[0], yRange[1]);
|
||||
}
|
||||
|
||||
Set<BlockPos> candidates = new LinkedHashSet<>();
|
||||
BlockPos adjustedPreferredPos = player.adjustSpawnLocation(destination, preferredPos);
|
||||
addArrivalCandidate(candidates, preferredPos);
|
||||
@@ -148,6 +154,40 @@ public final class AntTeleportHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static int[] getDimensionYRange(ServerLevel destination) {
|
||||
ResourceKey<Level> dim = destination.dimension();
|
||||
if (dim == AntarchySettings.termiteDestinationDimension()) {
|
||||
return new int[]{100, 200};
|
||||
}
|
||||
if (dim == AntarchySettings.brownAntDestinationDimension()) {
|
||||
return new int[]{75, 120};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Vec3 findSafeArrivalPositionInYRange(ServerPlayer player, ServerLevel destination, BlockPos preferredPos, int minY, int maxY) {
|
||||
for (int radius = 0; radius <= 8; radius++) {
|
||||
for (int xOff = -radius; xOff <= radius; xOff++) {
|
||||
for (int zOff = -radius; zOff <= radius; zOff++) {
|
||||
if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) {
|
||||
continue;
|
||||
}
|
||||
int x = preferredPos.getX() + xOff;
|
||||
int z = preferredPos.getZ() + zOff;
|
||||
for (int y = maxY; y >= minY + 1; y--) {
|
||||
Vec3 safePos = tryFindSafeDismount(player, destination, new BlockPos(x, y, z));
|
||||
if (safePos != null && safePos.y >= minY && safePos.y <= maxY) {
|
||||
return safePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void addArrivalCandidate(Set<BlockPos> candidates, @Nullable BlockPos pos) {
|
||||
if (pos == null) {
|
||||
return;
|
||||
|
||||
+2
-10
@@ -1,5 +1,6 @@
|
||||
package com.craisinlord.antarchy.content.entity.ant;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -91,16 +92,6 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canGroupWithNestmates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canMarch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldUseBiteAnimation() {
|
||||
return true;
|
||||
@@ -149,6 +140,7 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
this.triggerBiteAnimation(WOOD_BITE_ANIMATION_TICKS);
|
||||
this.level().destroyBlock(targetPos, false, this);
|
||||
this.level().playSound(null, targetPos, soundType.getBreakSound(), this.getSoundSource(), 0.7F, 1.1F);
|
||||
this.playSound(AntarchySoundEvents.ANT_BITE.get(), 0.35F, 0.95F + this.random.nextFloat() * 0.1F);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -604,7 +604,7 @@ public class BasiliskEntity extends Monster implements GeoEntity {
|
||||
BasiliskEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
}
|
||||
|
||||
// super handles path recalc and cooldown timer — always run it
|
||||
// super handles path recalc and cooldown timer
|
||||
super.tick();
|
||||
|
||||
if (BasiliskEntity.this.hissChargeTimer > 0 || BasiliskEntity.this.attackAnimTicks > 0) {
|
||||
|
||||
@@ -403,6 +403,17 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSeenByPlayer(net.minecraft.server.level.ServerPlayer player) {
|
||||
super.startSeenByPlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSeenByPlayer(net.minecraft.server.level.ServerPlayer player) {
|
||||
super.stopSeenByPlayer(player);
|
||||
this.bossEvent.removePlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Entity.RemovalReason reason) {
|
||||
this.clearKrakenStorm();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
public class CheepOnAStickItem extends Item {
|
||||
public CheepOnAStickItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
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<MenuType<DorrieMenu>> 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;
|
||||
}
|
||||
}
|
||||
+23
@@ -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 DorrieChargeJumpPayload(boolean pressing) implements CustomPacketPayload {
|
||||
public static final Type<DorrieChargeJumpPayload> TYPE =
|
||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dorrie_charge_jump_input"));
|
||||
public static final StreamCodec<ByteBuf, DorrieChargeJumpPayload> STREAM_CODEC =
|
||||
StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, DorrieChargeJumpPayload::pressing,
|
||||
DorrieChargeJumpPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<DorrieChargeJumpPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+23
@@ -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 DorrieJumpInputPayload(boolean pressing) implements CustomPacketPayload {
|
||||
public static final Type<DorrieJumpInputPayload> TYPE =
|
||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dorrie_jump_input"));
|
||||
public static final StreamCodec<ByteBuf, DorrieJumpInputPayload> STREAM_CODEC =
|
||||
StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, DorrieJumpInputPayload::pressing,
|
||||
DorrieJumpInputPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<DorrieJumpInputPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+67
-29
@@ -20,15 +20,19 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
net.minecraft.world.level.block.Block block,
|
||||
net.minecraft.world.level.block.Block coral,
|
||||
net.minecraft.world.level.block.Block wallFan,
|
||||
net.minecraft.world.level.block.Block floorFan
|
||||
net.minecraft.world.level.block.Block floorFan,
|
||||
net.minecraft.world.level.block.Block deadBlock,
|
||||
net.minecraft.world.level.block.Block deadCoral,
|
||||
net.minecraft.world.level.block.Block deadWallFan,
|
||||
net.minecraft.world.level.block.Block deadFloorFan
|
||||
) {}
|
||||
|
||||
private static final CoralSet[] CORAL_SETS = {
|
||||
new CoralSet(Blocks.TUBE_CORAL_BLOCK, Blocks.TUBE_CORAL, Blocks.TUBE_CORAL_WALL_FAN, Blocks.TUBE_CORAL_FAN),
|
||||
new CoralSet(Blocks.BRAIN_CORAL_BLOCK, Blocks.BRAIN_CORAL, Blocks.BRAIN_CORAL_WALL_FAN, Blocks.BRAIN_CORAL_FAN),
|
||||
new CoralSet(Blocks.BUBBLE_CORAL_BLOCK, Blocks.BUBBLE_CORAL, Blocks.BUBBLE_CORAL_WALL_FAN, Blocks.BUBBLE_CORAL_FAN),
|
||||
new CoralSet(Blocks.FIRE_CORAL_BLOCK, Blocks.FIRE_CORAL, Blocks.FIRE_CORAL_WALL_FAN, Blocks.FIRE_CORAL_FAN),
|
||||
new CoralSet(Blocks.HORN_CORAL_BLOCK, Blocks.HORN_CORAL, Blocks.HORN_CORAL_WALL_FAN, Blocks.HORN_CORAL_FAN),
|
||||
new CoralSet(Blocks.TUBE_CORAL_BLOCK, Blocks.TUBE_CORAL, Blocks.TUBE_CORAL_WALL_FAN, Blocks.TUBE_CORAL_FAN, Blocks.DEAD_TUBE_CORAL_BLOCK, Blocks.DEAD_TUBE_CORAL, Blocks.DEAD_TUBE_CORAL_WALL_FAN, Blocks.DEAD_TUBE_CORAL_FAN),
|
||||
new CoralSet(Blocks.BRAIN_CORAL_BLOCK, Blocks.BRAIN_CORAL, Blocks.BRAIN_CORAL_WALL_FAN, Blocks.BRAIN_CORAL_FAN, Blocks.DEAD_BRAIN_CORAL_BLOCK, Blocks.DEAD_BRAIN_CORAL, Blocks.DEAD_BRAIN_CORAL_WALL_FAN, Blocks.DEAD_BRAIN_CORAL_FAN),
|
||||
new CoralSet(Blocks.BUBBLE_CORAL_BLOCK, Blocks.BUBBLE_CORAL, Blocks.BUBBLE_CORAL_WALL_FAN, Blocks.BUBBLE_CORAL_FAN, Blocks.DEAD_BUBBLE_CORAL_BLOCK, Blocks.DEAD_BUBBLE_CORAL, Blocks.DEAD_BUBBLE_CORAL_WALL_FAN, Blocks.DEAD_BUBBLE_CORAL_FAN),
|
||||
new CoralSet(Blocks.FIRE_CORAL_BLOCK, Blocks.FIRE_CORAL, Blocks.FIRE_CORAL_WALL_FAN, Blocks.FIRE_CORAL_FAN, Blocks.DEAD_FIRE_CORAL_BLOCK, Blocks.DEAD_FIRE_CORAL, Blocks.DEAD_FIRE_CORAL_WALL_FAN, Blocks.DEAD_FIRE_CORAL_FAN),
|
||||
new CoralSet(Blocks.HORN_CORAL_BLOCK, Blocks.HORN_CORAL, Blocks.HORN_CORAL_WALL_FAN, Blocks.HORN_CORAL_FAN, Blocks.DEAD_HORN_CORAL_BLOCK, Blocks.DEAD_HORN_CORAL, Blocks.DEAD_HORN_CORAL_WALL_FAN, Blocks.DEAD_HORN_CORAL_FAN),
|
||||
};
|
||||
|
||||
public CoralSpikeFeature(Codec<NoneFeatureConfiguration> codec) {
|
||||
@@ -58,13 +62,13 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
|
||||
CoralSet coral = CORAL_SETS[random.nextInt(CORAL_SETS.length)];
|
||||
|
||||
int baseRadius = 2 + random.nextInt(2); // 2–3 wide at base
|
||||
int totalHeight = 20 + random.nextInt(15); // 20–34 blocks tall
|
||||
int baseRadius = 2 + random.nextInt(2); // 2-3 wide at base
|
||||
int totalHeight = 20 + random.nextInt(15); // 20-34 blocks tall
|
||||
|
||||
double curveAngle = random.nextDouble() * Math.PI * 2;
|
||||
double curveDX = Math.cos(curveAngle);
|
||||
double curveDZ = Math.sin(curveAngle);
|
||||
double maxCurve = 4.0 + random.nextDouble() * 5.0; // 4–9 block lean at tip
|
||||
double maxCurve = 4.0 + random.nextDouble() * 5.0; // 4-9 block lean at tip
|
||||
|
||||
boolean placed = false;
|
||||
|
||||
@@ -72,7 +76,7 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
int blockY = floorY + dy;
|
||||
double progress = (double) dy / totalHeight;
|
||||
|
||||
// Quadratic lean — slow at base, bending hard near tip
|
||||
// Quadratic lean -- slow at base, bending hard near tip
|
||||
double curveOffset = maxCurve * progress * progress;
|
||||
double centerX = origin.getX() + curveDX * curveOffset;
|
||||
double centerZ = origin.getZ() + curveDZ * curveOffset;
|
||||
@@ -92,7 +96,11 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
BlockPos pos = new BlockPos(origin.getX() + dx, blockY, origin.getZ() + dz);
|
||||
if (!canReplace(level.getBlockState(pos))) continue;
|
||||
|
||||
level.setBlock(pos, coral.block().defaultBlockState(), 2);
|
||||
boolean inWater = touchesWater(level, pos);
|
||||
level.setBlock(pos, inWater
|
||||
? coral.block().defaultBlockState()
|
||||
: coral.deadBlock().defaultBlockState(),
|
||||
2);
|
||||
placed = true;
|
||||
|
||||
if (dist >= radius - 0.6 && radius > 0) {
|
||||
@@ -115,11 +123,19 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
BlockState existing = level.getBlockState(fanPos);
|
||||
if (!existing.isAir() && !existing.is(Blocks.WATER)) continue;
|
||||
boolean fanInWater = existing.is(Blocks.WATER);
|
||||
level.setBlock(fanPos,
|
||||
coral.wallFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.HORIZONTAL_FACING, dir)
|
||||
.setValue(BlockStateProperties.WATERLOGGED, fanInWater),
|
||||
2);
|
||||
if (fanInWater) {
|
||||
level.setBlock(fanPos,
|
||||
coral.wallFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.HORIZONTAL_FACING, dir)
|
||||
.setValue(BlockStateProperties.WATERLOGGED, true),
|
||||
2);
|
||||
} else {
|
||||
level.setBlock(fanPos,
|
||||
coral.deadWallFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.HORIZONTAL_FACING, dir)
|
||||
.setValue(BlockStateProperties.WATERLOGGED, false),
|
||||
2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,22 +158,36 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
float roll = random.nextFloat();
|
||||
CoralSet randomCoral = CORAL_SETS[random.nextInt(CORAL_SETS.length)];
|
||||
|
||||
if (roll < 0.35f && decorInWater) {
|
||||
// Coral plant (frond)
|
||||
BlockState coralState = randomCoral.coral().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, true);
|
||||
if (coralState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, coralState, 2);
|
||||
if (roll < 0.35f) {
|
||||
if (decorInWater) {
|
||||
BlockState coralState = randomCoral.coral().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, true);
|
||||
if (coralState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, coralState, 2);
|
||||
}
|
||||
} else {
|
||||
BlockState coralState = randomCoral.deadCoral().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, false);
|
||||
if (coralState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, coralState, 2);
|
||||
}
|
||||
}
|
||||
} else if (roll < 0.65f) {
|
||||
// Floor fan
|
||||
BlockState fanState = randomCoral.floorFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, decorInWater);
|
||||
if (fanState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, fanState, 2);
|
||||
if (decorInWater) {
|
||||
BlockState fanState = randomCoral.floorFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, true);
|
||||
if (fanState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, fanState, 2);
|
||||
}
|
||||
} else {
|
||||
BlockState fanState = randomCoral.deadFloorFan().defaultBlockState()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, false);
|
||||
if (fanState.canSurvive(level, decorPos)) {
|
||||
level.setBlock(decorPos, fanState, 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Sea pickle cluster
|
||||
} else if (decorInWater) {
|
||||
// Sea pickle cluster -- only underwater
|
||||
BlockState pickle = Blocks.SEA_PICKLE.defaultBlockState()
|
||||
.setValue(SeaPickleBlock.PICKLES, 1 + random.nextInt(4));
|
||||
if (pickle.canSurvive(level, decorPos)) {
|
||||
@@ -169,6 +199,14 @@ public class CoralSpikeFeature extends Feature<NoneFeatureConfiguration> {
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static boolean touchesWater(WorldGenLevel level, BlockPos pos) {
|
||||
if (level.getBlockState(pos).is(Blocks.WATER)) return true;
|
||||
for (Direction dir : Direction.values()) {
|
||||
if (level.getBlockState(pos.relative(dir)).is(Blocks.WATER)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canReplace(BlockState state) {
|
||||
return state.isAir() || state.is(Blocks.WATER) || state.canBeReplaced();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user