package net.minecraft.world.entity.animal; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.FluidTags; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.control.MoveControl; import net.minecraft.world.entity.ai.goal.AvoidEntityGoal; import net.minecraft.world.entity.ai.goal.PanicGoal; import net.minecraft.world.entity.ai.goal.RandomSwimmingGoal; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.navigation.WaterBoundPathNavigation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; public abstract class AbstractFish extends WaterAnimal implements Bucketable { private static final EntityDataAccessor FROM_BUCKET = SynchedEntityData.defineId(AbstractFish.class, EntityDataSerializers.BOOLEAN); private static final boolean DEFAULT_FROM_BUCKET = false; public AbstractFish(EntityType p_27461_, Level p_27462_) { super(p_27461_, p_27462_); this.moveControl = new AbstractFish.FishMoveControl(this); } public static AttributeSupplier.Builder createAttributes() { return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 3.0); } @Override public boolean requiresCustomPersistence() { return super.requiresCustomPersistence() || this.fromBucket(); } @Override public boolean removeWhenFarAway(double p_27492_) { return !this.fromBucket() && !this.hasCustomName(); } @Override public int getMaxSpawnClusterSize() { return 8; } @Override protected void defineSynchedData(SynchedEntityData.Builder p_334315_) { super.defineSynchedData(p_334315_); p_334315_.define(FROM_BUCKET, false); } @Override public boolean fromBucket() { return this.entityData.get(FROM_BUCKET); } @Override public void setFromBucket(boolean p_27498_) { this.entityData.set(FROM_BUCKET, p_27498_); } @Override public void addAdditionalSaveData(CompoundTag p_27485_) { super.addAdditionalSaveData(p_27485_); p_27485_.putBoolean("FromBucket", this.fromBucket()); } @Override public void readAdditionalSaveData(CompoundTag p_27465_) { super.readAdditionalSaveData(p_27465_); this.setFromBucket(p_27465_.getBooleanOr("FromBucket", false)); } @Override protected void registerGoals() { super.registerGoals(); this.goalSelector.addGoal(0, new PanicGoal(this, 1.25)); this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6, 1.4, EntitySelector.NO_SPECTATORS::test)); this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this)); } @Override protected PathNavigation createNavigation(Level p_27480_) { return new WaterBoundPathNavigation(this, p_27480_); } @Override public void travel(Vec3 p_27490_) { if (this.isInWater()) { this.moveRelative(0.01F, p_27490_); this.move(MoverType.SELF, this.getDeltaMovement()); this.setDeltaMovement(this.getDeltaMovement().scale(0.9)); if (this.getTarget() == null) { this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.005, 0.0)); } } else { super.travel(p_27490_); } } @Override public void aiStep() { if (!this.isInWater() && this.onGround() && this.verticalCollision) { this.setDeltaMovement(this.getDeltaMovement().add((this.random.nextFloat() * 2.0F - 1.0F) * 0.05F, 0.4F, (this.random.nextFloat() * 2.0F - 1.0F) * 0.05F)); this.setOnGround(false); this.hasImpulse = true; this.makeSound(this.getFlopSound()); } super.aiStep(); } @Override protected InteractionResult mobInteract(Player p_27477_, InteractionHand p_27478_) { return Bucketable.bucketMobPickup(p_27477_, p_27478_, this).orElse(super.mobInteract(p_27477_, p_27478_)); } @Override public void saveToBucketTag(ItemStack p_27494_) { Bucketable.saveDefaultDataToBucketTag(this, p_27494_); } @Override public void loadFromBucketTag(CompoundTag p_148708_) { Bucketable.loadDefaultDataFromBucketTag(this, p_148708_); } @Override public SoundEvent getPickupSound() { return SoundEvents.BUCKET_FILL_FISH; } protected boolean canRandomSwim() { return true; } protected abstract SoundEvent getFlopSound(); @Override protected SoundEvent getSwimSound() { return SoundEvents.FISH_SWIM; } @Override protected void playStepSound(BlockPos p_27482_, BlockState p_27483_) { } static class FishMoveControl extends MoveControl { private final AbstractFish fish; FishMoveControl(AbstractFish p_27501_) { super(p_27501_); this.fish = p_27501_; } @Override public void tick() { if (this.fish.isEyeInFluid(FluidTags.WATER)) { this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, 0.005, 0.0)); } if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) { float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f)); double d0 = this.wantedX - this.fish.getX(); double d1 = this.wantedY - this.fish.getY(); double d2 = this.wantedZ - this.fish.getZ(); if (d1 != 0.0) { double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, this.fish.getSpeed() * (d1 / d3) * 0.1, 0.0)); } if (d0 != 0.0 || d2 != 0.0) { float f1 = (float)(Mth.atan2(d2, d0) * 180.0F / (float)Math.PI) - 90.0F; this.fish.setYRot(this.rotlerp(this.fish.getYRot(), f1, 90.0F)); this.fish.yBodyRot = this.fish.getYRot(); } } else { this.fish.setSpeed(0.0F); } } } static class FishSwimGoal extends RandomSwimmingGoal { private final AbstractFish fish; public FishSwimGoal(AbstractFish p_27505_) { super(p_27505_, 1.0, 40); this.fish = p_27505_; } @Override public boolean canUse() { return this.fish.canRandomSwim() && super.canUse(); } } }