Энтити перестаёт двигаться вообще после перезахода в мир

Версия Minecraft
1.20.1
API
Forge
43
5
1
Всем здаров! Делаю сейчас мод на животных, написал что-то типа кастомного поведения для утки, используя код черепахи + небольшую самописную цель, и столкнулся с проблемой: когда спавнишь утку, всё окей, плавает по воде, ходит по суше, етц. Но как только перезаходишь в мир, всё, кирдык, держится ровно на воде и только корпусом в разные стороны вертит. В чём может быть проблема, и как вы бы решили эту проблему?
P.S подход с черепахой (сохранение в данных энтити координат, куда оно двигалось) не пробовал, не хочу слишком усложнять.


Код утки:

Java:
package net.epichunt.entity.animals;

import com.google.common.base.Suppliers;
import net.epichunt.entity.SurfaceSwimGoal;
import net.epichunt.sound.Sounds;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.*;
import net.minecraft.world.entity.ai.navigation.AmphibiousPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.animal.Animal;
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.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;

import java.util.function.Supplier;

public class DuckEntity extends Animal {

    public DuckEntity(EntityType<? extends Animal> entityType, Level level) {
        super(entityType, level);
    }

    public static final Supplier<EntityType<DuckEntity>> DUCK = Suppliers.memoize(() -> EntityType.Builder.of(DuckEntity::new, MobCategory.CREATURE)
            .sized(0.7f, 0.7f).build("duck"));

    @Override
    protected void updateWalkAnimation(float partialTick) {
        float f;
        if (this.getPose() == Pose.STANDING) {
            f = Math.min(partialTick * 6F, 1f);
        } else {
            f = 0f;
        }
        this.walkAnimation.update(f, 0.4f);
    }

    @Override
    protected void registerGoals() {
        this.goalSelector.addGoal(1, new PanicGoal(this, 1.7));
        this.goalSelector.addGoal(2, new FollowParentGoal(this, 1.1D));
        this.goalSelector.addGoal(4, new SurfaceSwimGoal(this));
        this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1f, 10));
        this.goalSelector.addGoal(6, new DuckRandomStrollGoal(this, 6f, 10));
        this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 3f));
        this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
    }

    protected PathNavigation createNavigation(Level arg) {
        return new DuckNavigation(this, arg);
    }

    public static AttributeSupplier.Builder createAttributes() {
        return Animal.createLivingAttributes().add(Attributes.MAX_HEALTH, 4D).add(Attributes.MOVEMENT_SPEED, 0.23000000298023224)
                .add(Attributes.FOLLOW_RANGE, 25D);
    }

    private static class DuckNavigation extends AmphibiousPathNavigation {
        DuckNavigation(Mob mob, Level level) {
            super(mob, level);
        }

        @Override
        public boolean isStableDestination(BlockPos pos) {
            if (this.mob.isInWater()) {
                return this.level.getBlockState(pos).is(Blocks.WATER); // Вода
            } else {
                return !this.level.getBlockState(pos.below()).isAir(); // Суша
            }
        }
    }

    @Nullable
    @Override
    public AgeableMob getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) {
        return DUCK.get().create(serverLevel);
    }

    @Override
    public SoundEvent getEatingSound(ItemStack itemStack) {
        return Sounds.DEER_EAT.get();
    }

    @Nullable
    @Override
    protected SoundEvent getAmbientSound() {
        return Sounds.DEER_AMBIENT.get();
    }

    @Nullable
    @Override
    protected SoundEvent getHurtSound(DamageSource damageSource) {
        return Sounds.DEER_HURT.get();
    }

    @Nullable
    @Override
    protected SoundEvent getDeathSound() {
        return Sounds.DEER_DEATH.get();
    }

    protected void playStepSound(BlockPos blockPos, BlockState blockState) {
        this.playSound(SoundEvents.CHICKEN_STEP, 0.15F, 1.0F);
    }

    protected float getSoundVolume() {
        return 0.3F;
    }

    public class DuckRandomStrollGoal extends RandomStrollGoal {
        private final DuckEntity duck;
        DuckRandomStrollGoal(DuckEntity arg, double d, int i) {
            super(arg, d, i);
            this.duck = arg;
        }

        public boolean canUse() {
            return this.mob.isInWater() ? super.canUse() : false;
        }
    }
}


кастомная цель удержания на воде:
Java:
package net.epichunt.entity;

import net.minecraft.world.entity.Mob;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.phys.Vec3;

public class SurfaceSwimGoal extends Goal {
    private final Mob mob;

    public SurfaceSwimGoal(Mob mob) {
        this.mob = mob;
    }

    @Override
    public boolean canUse() {
        return mob.isInWater() && mob.getFluidHeight(FluidTags.WATER) > mob.getFluidJumpThreshold();
    }

    @Override
    public boolean requiresUpdateEveryTick() {
        return true; 
    }

    @Override
    public void tick() {
        double surfaceY = mob.getFluidHeight(FluidTags.WATER) + mob.getBlockY();
        if (mob.getY() < surfaceY) {
            Vec3 currentMovement = mob.getDeltaMovement();
            double verticalSpeed = Math.min(0.1, surfaceY - mob.getY());
            mob.setDeltaMovement(currentMovement.x, verticalSpeed, currentMovement.z);
        }
    }
}
 
Последнее редактирование:
Решение
Типа через Home/Travel?
Без home(он для позиции 'дома' черепахи), нужен EntityDataAccessor<BlockPos> TRAVELкоторый сеттиш в move/travel/SurfaceSwimGoal#tick(не знаю где лучше будет) и используешь в SurfaceSwimGoal#tick.

чёт ну жёстко как будто бы
Нармальна
EntityDataAccessor(и сохранение в nbt) - самая база для entity.
Типа через Home/Travel?
Без home(он для позиции 'дома' черепахи), нужен EntityDataAccessor<BlockPos> TRAVELкоторый сеттиш в move/travel/SurfaceSwimGoal#tick(не знаю где лучше будет) и используешь в SurfaceSwimGoal#tick.

чёт ну жёстко как будто бы
Нармальна
EntityDataAccessor(и сохранение в nbt) - самая база для entity.
 
Назад
Сверху