Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package com.mysticsbiomes.common.entity;
- import com.mysticsbiomes.init.MysticEntities;
- import io.netty.buffer.ByteBuf;
- import net.minecraft.core.BlockPos;
- import net.minecraft.nbt.CompoundTag;
- import net.minecraft.network.codec.ByteBufCodecs;
- import net.minecraft.network.codec.StreamCodec;
- import net.minecraft.network.syncher.EntityDataAccessor;
- import net.minecraft.network.syncher.EntityDataSerializer;
- import net.minecraft.network.syncher.EntityDataSerializers;
- import net.minecraft.network.syncher.SynchedEntityData;
- import net.minecraft.server.level.ServerLevel;
- import net.minecraft.tags.FluidTags;
- import net.minecraft.tags.ItemTags;
- import net.minecraft.util.ByIdMap;
- import net.minecraft.util.Mth;
- import net.minecraft.util.StringRepresentable;
- import net.minecraft.world.DifficultyInstance;
- import net.minecraft.world.InteractionHand;
- import net.minecraft.world.InteractionResult;
- 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.behavior.BehaviorUtils;
- import net.minecraft.world.entity.ai.control.BodyRotationControl;
- import net.minecraft.world.entity.ai.control.LookControl;
- import net.minecraft.world.entity.ai.control.MoveControl;
- import net.minecraft.world.entity.ai.goal.*;
- 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.level.Level;
- import net.minecraft.world.level.LevelReader;
- import net.minecraft.world.level.ServerLevelAccessor;
- import net.minecraft.world.level.block.Blocks;
- import net.minecraft.world.level.block.state.BlockState;
- import net.minecraft.world.level.pathfinder.AmphibiousNodeEvaluator;
- import net.minecraft.world.level.pathfinder.Path;
- import net.minecraft.world.level.pathfinder.PathFinder;
- import net.minecraft.world.level.pathfinder.PathType;
- import net.minecraft.world.phys.Vec3;
- import javax.annotation.Nullable;
- import java.util.Optional;
- import java.util.function.IntFunction;
- /**
- * can be tamed as pets to defend you in the water.
- * they can help you find treasures, or collect fish.
- * they can help you swim faster like dolphins.
- * they mostly float around, unless they are feeling playful or want to get food.
- * they will fall asleep at night, holding another sea otters hand nearby.
- * their babies will rest on their belly until they age up.
- */
- public class SeaOtter extends Animal {
- public static final EntityDataSerializer<SeaOtter.State> SEA_OTTER_STATE = EntityDataSerializer.forValueType(SeaOtter.State.STREAM_CODEC);
- private static final EntityDataAccessor<SeaOtter.State> DATA_STATE_ID = SynchedEntityData.defineId(SeaOtter.class, SEA_OTTER_STATE);
- private static final EntityDataAccessor<Boolean> DATA_FLOATING_ID = SynchedEntityData.defineId(SeaOtter.class, EntityDataSerializers.BOOLEAN);
- private static final EntityDataAccessor<Boolean> DATA_SITTING_ID = SynchedEntityData.defineId(SeaOtter.class, EntityDataSerializers.BOOLEAN);
- private boolean needsToSurface;
- private int ticksSinceLastSwamAround;
- private int ticksOutOfWater;
- @Nullable
- private BlockPos surfacePos;
- public final AnimationState idleInWaterAnimationState = new AnimationState();
- public final AnimationState swimmingAnimationState = new AnimationState();
- public final AnimationState glideWhileSwimmingAnimationState = new AnimationState();
- public final AnimationState twirlWhileSwimmingAnimationState = new AnimationState();
- public final AnimationState floatingAnimationState = new AnimationState();
- public final AnimationState swimmingWhileFloatingAnimationState = new AnimationState();
- public final AnimationState twirlWhileFloatingAnimationState = new AnimationState();
- public final AnimationState startFloatingAnimationState = new AnimationState();
- public final AnimationState stopFloatingAnimationState = new AnimationState();
- public final AnimationState sitDownAnimationState = new AnimationState();
- public final AnimationState sittingAnimationState = new AnimationState();
- public final AnimationState standUpAnimationState = new AnimationState();
- public final AnimationState walkingAnimationState = new AnimationState();
- private long inStateTicks = 0L;
- public SeaOtter(EntityType<? extends SeaOtter> type, Level level) {
- super(type, level);
- this.setPathfindingMalus(PathType.WATER, 0.0F);
- this.moveControl = new SeaOtter.SeaOtterMoveControl(this);
- this.lookControl = new SeaOtter.SeaOtterLookControl(this);
- }
- public static AttributeSupplier.Builder createAttributes() {
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 16.0F).add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.STEP_HEIGHT, 1.0);
- }
- @Override
- protected void registerGoals() {
- this.goalSelector.addGoal(0, new SeaOtter.SwimToSurfaceGoal(this));
- this.goalSelector.addGoal(1, new TemptGoal(this, 0.2D, (stack) -> stack.is(ItemTags.FISHES), false));
- this.goalSelector.addGoal(2, new LookAtPlayerGoal(this, Player.class, 6.0F));
- this.goalSelector.addGoal(3, new RandomLookAroundGoal(this));
- this.goalSelector.addGoal(4, new SeaOtter.FloatingGoal(this));
- this.goalSelector.addGoal(5, new SeaOtter.MoveToWaterGoal(this, 1.0D));
- this.goalSelector.addGoal(6, new SeaOtter.SwimAroundGoal(this, 1.0D, 10));
- }
- @Override
- protected void defineSynchedData(SynchedEntityData.Builder builder) {
- super.defineSynchedData(builder);
- builder.define(DATA_STATE_ID, SeaOtter.State.NOTHING);
- builder.define(DATA_FLOATING_ID, false);
- builder.define(DATA_SITTING_ID, false);
- }
- @Override
- public void addAdditionalSaveData(CompoundTag tag) {
- super.addAdditionalSaveData(tag);
- tag.putString("State", this.getState().getSerializedName());
- tag.putBoolean("Floating", this.isFloating());
- tag.putBoolean("Sitting", this.isSitting());
- tag.putBoolean("NeedsToSurface", this.needsToSurface());
- tag.putInt("TicksSinceSwam", this.ticksSinceLastSwamAround);
- tag.putInt("TicksOutOfWater", this.ticksOutOfWater);
- }
- @Override
- public void readAdditionalSaveData(CompoundTag tag) {
- super.readAdditionalSaveData(tag);
- this.switchToState(SeaOtter.State.fromName(tag.getString("State")));
- this.setFloating(tag.getBoolean("Floating"));
- this.setSitting(tag.getBoolean("Sitting"));
- this.setNeedsToSurface(tag.getBoolean("NeedsToSurface"));
- this.ticksSinceLastSwamAround = tag.getInt("TicksSinceSwam");
- this.ticksOutOfWater = tag.getInt("TicksOutOfWater");
- }
- @Override
- public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
- return MysticEntities.SEA_OTTER.get().create(level);
- }
- @Override
- public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance instance, MobSpawnType type, SpawnGroupData data) {
- return super.finalizeSpawn(level, instance, type, data);
- }
- @Override
- public int getMaxAirSupply() {
- return 6000; /// 5 minutes
- }
- @Override
- public void tick() {
- super.tick();
- if (this.isFloating()) {
- this.wasTouchingWater = true;
- this.setAirSupply(this.getMaxAirSupply());
- this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
- }
- if (!this.level().isClientSide) {
- if (this.isFloating()) {
- ++this.ticksSinceLastSwamAround;
- }
- if (!this.isFloating() && !this.isInWater()) {
- ++this.ticksOutOfWater;
- }
- if (this.tickCount % 60 == 0) {
- System.out.println("state: " + this.getState() + " isFloating: " + this.isFloating() + " isSitting: " + this.isSitting() + " ticksSinceLastSwamAround: " + this.ticksSinceLastSwamAround + " wantsToSwim: " + this.wantsToSwim());
- }
- }
- if (this.level().isClientSide()) {
- this.setupSwimmingAnimationStates();
- this.setupFloatingAnimationStates();
- this.setupSittingAnimationStates();
- }
- this.inStateTicks++;
- }
- private void setupSwimmingAnimationStates() {
- switch (this.getState()) {
- case IDLE_IN_WATER:
- this.swimmingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.walkingAnimationState.stop();
- this.sittingAnimationState.stop();
- this.standUpAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.idleInWaterAnimationState.startIfStopped(this.tickCount);
- break;
- case SWIMMING:
- this.idleInWaterAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.walkingAnimationState.stop();
- this.sittingAnimationState.stop();
- this.standUpAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.swimmingAnimationState.startIfStopped(this.tickCount);
- break;
- case GLIDE_WHILE_SWIMMING:
- this.swimmingAnimationState.stop();
- this.glideWhileSwimmingAnimationState.startIfStopped(this.tickCount);
- break;
- case TWIRL_WHILE_SWIMMING:
- this.twirlWhileSwimmingAnimationState.startIfStopped(this.tickCount);
- break;
- }
- }
- private void setupFloatingAnimationStates() {
- switch (this.getState()) {
- case START_FLOATING:
- this.idleInWaterAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.startIfStopped(this.tickCount);
- break;
- case FLOATING:
- this.idleInWaterAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.floatingAnimationState.startIfStopped(this.tickCount);
- break;
- case STOP_FLOATING:
- this.idleInWaterAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.startIfStopped(this.tickCount);
- break;
- case SWIMMING_WHILE_FLOATING:
- this.swimmingWhileFloatingAnimationState.startIfStopped(this.tickCount);
- break;
- case TWIRL_WHILE_FLOATING:
- this.twirlWhileFloatingAnimationState.startIfStopped(this.tickCount);
- break;
- }
- }
- private void setupSittingAnimationStates() {
- switch (this.getState()) {
- case SIT_DOWN:
- this.standUpAnimationState.stop();
- this.sittingAnimationState.stop();
- this.walkingAnimationState.stop();
- this.sitDownAnimationState.startIfStopped(this.tickCount);
- break;
- case SITTING:
- this.standUpAnimationState.stop();
- this.walkingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingAnimationState.startIfStopped(this.tickCount);
- break;
- case STAND_UP:
- this.sittingAnimationState.stop();
- this.walkingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.standUpAnimationState.startIfStopped(this.tickCount);
- break;
- case WALKING:
- this.sitDownAnimationState.stop();
- this.sittingAnimationState.stop();
- this.standUpAnimationState.stop();
- this.walkingAnimationState.startIfStopped(this.tickCount);
- break;
- }
- }
- @Override
- public void aiStep() {
- super.aiStep();
- if (this.isEffectiveAi() && this.isAlive()) {
- if (!this.needsToSurface() && this.isUnderWater() && (!this.wantsToSwim() || this.getAirSupply() < 400)) {
- this.setNeedsToSurface(true);
- System.out.println("needs to surface");
- }
- }
- if (!this.level().isClientSide()) {
- if (this.onGround() && !this.isInWater() && !this.isFloating()) {
- if (this.wantsToMove()) {
- if (this.isSitting() || this.getState() == State.SIT_DOWN) {
- if (this.getState() != State.STAND_UP) {
- this.switchToState(State.STAND_UP);
- this.setSitting(false);
- }
- }
- if (this.getState() == State.STAND_UP && this.inStateTicks >= State.STAND_UP.animationDuration()) {
- this.switchToState(State.WALKING);
- }
- if (!this.isSitting() && this.getState() != State.STAND_UP && this.getState() != State.WALKING) {
- this.switchToState(State.WALKING);
- }
- } else {
- if (!this.isSitting() && this.getState() != State.SIT_DOWN) {
- this.switchToState(State.SIT_DOWN);
- this.setSitting(true);
- } else if (this.getState() == State.SIT_DOWN && this.inStateTicks >= State.SIT_DOWN.animationDuration()) {
- this.switchToState(State.SITTING);
- this.setSitting(true);
- }
- if (this.getState() == State.STAND_UP && this.inStateTicks >= State.STAND_UP.animationDuration()) {
- this.switchToState(State.SIT_DOWN);
- this.setSitting(true);
- }
- }
- }
- }
- }
- @Override
- public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
- if (DATA_STATE_ID.equals(accessor)) {
- this.inStateTicks = 0L;
- }
- super.onSyncedDataUpdated(accessor);
- }
- public SeaOtter.State getState() {
- return this.entityData.get(DATA_STATE_ID);
- }
- public void switchToState(SeaOtter.State state) {
- this.entityData.set(DATA_STATE_ID, state);
- this.inStateTicks = 0L;
- }
- public boolean isInAnimationTransition() {
- return switch (this.getState()) {
- case START_FLOATING -> this.inStateTicks < State.START_FLOATING.animationDuration();
- case STOP_FLOATING -> this.inStateTicks < State.STOP_FLOATING.animationDuration();
- case SIT_DOWN -> this.inStateTicks < State.SIT_DOWN.animationDuration();
- case STAND_UP -> this.inStateTicks < State.STAND_UP.animationDuration();
- default -> false;
- };
- }
- public boolean isFloating() {
- return this.entityData.get(DATA_FLOATING_ID);
- }
- public void setFloating(boolean value) {
- this.entityData.set(DATA_FLOATING_ID, value);
- }
- public boolean isSitting() {
- return this.entityData.get(DATA_SITTING_ID);
- }
- public void setSitting(boolean value) {
- this.entityData.set(DATA_SITTING_ID, value);
- }
- public void sitDown() {
- if (!this.isSitting() && this.onGround() && !this.isInWater()) {
- this.switchToState(State.SIT_DOWN);
- this.setSitting(true);
- }
- }
- public void standUp() {
- if (this.isSitting()) {
- this.switchToState(State.STAND_UP);
- this.setSitting(false);
- }
- }
- public boolean canSwim() {
- return this.isInWater() || !this.needsToSurface() || !this.isFloating();
- }
- public boolean wantsToSwim() {
- return this.ticksSinceLastSwamAround > 200; /// 1 minute
- }
- public void resetTicksSinceLastSwam() {
- this.ticksSinceLastSwamAround = 0;
- }
- public boolean needsToSurface() {
- return this.needsToSurface;
- }
- public void setNeedsToSurface(boolean value) {
- this.needsToSurface = value;
- }
- @Nullable
- public BlockPos getSurfacePos() {
- return this.surfacePos;
- }
- public void setSurfacePos(@Nullable BlockPos pos) {
- this.surfacePos = pos;
- }
- public boolean wantsToGoInWater() {
- return this.ticksOutOfWater > 200;
- }
- public void resetTicksOutOfWater() {
- this.ticksOutOfWater = 0;
- }
- @Override
- protected PathNavigation createNavigation(Level level) {
- return new SeaOtter.SeaOtterPathNavigation(this, level);
- }
- @Override
- protected BodyRotationControl createBodyControl() {
- return new SeaOtter.SeaOtterBodyRotationControl(this);
- }
- @Override
- public void travel(Vec3 pos) {
- if (this.refuseToMove() && this.onGround()) {
- this.setDeltaMovement(this.getDeltaMovement().multiply(0.0, 1.0, 0.0));
- pos = pos.multiply(0.0, 1.0, 0.0);
- }
- super.travel(pos);
- }
- public boolean refuseToMove() {
- return this.isSitting() || this.isInAnimationTransition();
- }
- public boolean wantsToMove() {
- return !this.getNavigation().isDone() || this.getTarget() != null || this.moveControl.hasWanted();
- }
- public boolean isCloserThan(BlockPos pos, double distance) {
- return pos.closerThan(this.blockPosition(), distance);
- }
- public boolean hasReachedTarget(BlockPos pos, double distance) {
- if (this.isCloserThan(pos, distance)) {
- return true;
- } else {
- Path path = this.navigation.getPath();
- return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
- }
- }
- public void pathfindDirectlyTowards(BlockPos pos, double speed) {
- this.navigation.setMaxVisitedNodesMultiplier(10.0F);
- this.navigation.moveTo(pos.getX(), pos.getY(), pos.getZ(), speed);
- if (this.navigation.getPath() != null) {
- this.navigation.getPath().canReach();
- }
- }
- @Override
- public Vec3 getViewVector(float partialTicks) {
- Vec3 view = super.getViewVector(partialTicks);
- if (this.isFloating()) {
- return view.reverse();
- }
- return view;
- }
- @Override
- public int getMaxHeadXRot() {
- return super.getMaxHeadXRot();
- }
- @Override
- public int getMaxHeadYRot() {
- if (this.isFloating()) {
- return 2;
- } else if (this.isSitting()) {
- return 20;
- } else {
- return super.getMaxHeadYRot();
- }
- }
- @Override
- public int getHeadRotSpeed() {
- return this.isFloating() ? 5 : 10;
- }
- @Override
- public InteractionResult mobInteract(Player player, InteractionHand hand) {
- return super.mobInteract(player, hand);
- }
- @Override
- public boolean isFood(ItemStack stack) {
- return false;
- }
- public static class SwimAroundGoal extends RandomSwimmingGoal {
- private final SeaOtter seaOtter;
- private int ticksSwimming;
- public SwimAroundGoal(SeaOtter mob, double speed, int interval) {
- super(mob, speed, interval);
- this.seaOtter = mob;
- }
- @Override
- public boolean canUse() {
- if (!this.seaOtter.isInWater()) {
- return false;
- } else if (this.seaOtter.isFloating()) {
- return false;
- } else if (this.seaOtter.needsToSurface()) {
- return false;
- }
- return this.seaOtter.wantsToSwim() && super.canUse();
- }
- @Override
- public boolean canContinueToUse() {
- return !this.seaOtter.needsToSurface() && (this.seaOtter.wantsToSwim() || this.ticksSwimming <= 200);
- }
- @Override
- public void start() {
- super.start();
- this.seaOtter.switchToState(State.SWIMMING);
- }
- @Override
- public void stop() {
- super.stop();
- this.ticksSwimming = 0;
- this.seaOtter.setNeedsToSurface(true);
- this.seaOtter.resetTicksSinceLastSwam();
- }
- @Override
- protected Vec3 getPosition() {
- return BehaviorUtils.getRandomSwimmablePos(this.mob, 10, 4);
- }
- @Override
- public void tick() {
- if (this.seaOtter.needsToSurface()) {
- this.stop();
- return;
- }
- this.trigger();
- ++this.ticksSwimming;
- }
- }
- public static class MoveToWaterGoal extends MoveToBlockGoal {
- private final SeaOtter seaOtter;
- public MoveToWaterGoal(SeaOtter mob, double speed) {
- super(mob, speed, 24);
- this.seaOtter = mob;
- this.verticalSearchStart = -1;
- }
- @Override
- public boolean canUse() {
- return !this.seaOtter.isInWater() && this.seaOtter.wantsToGoInWater() && super.canUse();
- }
- @Override
- public boolean canContinueToUse() {
- return !this.seaOtter.isInWater() && this.tryTicks <= 1200 && this.isValidTarget(this.seaOtter.level(), this.blockPos);
- }
- @Override
- public boolean shouldRecalculatePath() {
- return this.tryTicks % 160 == 0;
- }
- @Override
- protected boolean isValidTarget(LevelReader level, BlockPos pos) {
- return level.getBlockState(pos).is(Blocks.WATER);
- }
- }
- public static class SwimToSurfaceGoal extends MoveToBlockGoal {
- private final SeaOtter seaOtter;
- private boolean reachedTarget;
- public SwimToSurfaceGoal(SeaOtter mob) {
- super(mob, 1.0D, 24, 24);
- this.seaOtter = mob;
- }
- @Override
- public boolean canUse() {
- if (!this.seaOtter.isInWater() && this.seaOtter.wantsToSwim() && this.seaOtter.isFloating()) {
- return false;
- }
- return this.seaOtter.needsToSurface() && super.canUse();
- }
- @Override
- public boolean canContinueToUse() {
- return !this.isReachedTarget();
- }
- @Override
- public void start() {
- super.start();
- this.seaOtter.switchToState(State.SWIMMING);
- this.seaOtter.setSurfacePos(this.blockPos);
- }
- @Override
- public void stop() {
- this.seaOtter.setNeedsToSurface(false);
- this.seaOtter.getNavigation().stop();
- }
- @Override
- public void tick() {
- BlockPos pos = this.getMoveToTarget();
- Vec3 otterPos = this.seaOtter.position();
- Vec3 targetCenter = Vec3.atCenterOf(this.blockPos.above());
- if (Math.abs(targetCenter.x - otterPos.x) < 0.5 && Math.abs(targetCenter.z - otterPos.z) < 0.5) {
- Vec3 motion = targetCenter.subtract(otterPos).normalize().scale(0.075D);
- this.seaOtter.setDeltaMovement(motion);
- this.seaOtter.getLookControl().setLookAt(pos.getX(), pos.getY(), pos.getZ());
- }
- if (!pos.closerToCenterThan(this.seaOtter.position(), this.acceptedDistance())) {
- this.seaOtter.pathfindDirectlyTowards(pos,3.0D);
- this.seaOtter.getLookControl().setLookAt(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, (float)(this.seaOtter.getMaxHeadYRot() + 20), (float)this.seaOtter.getMaxHeadXRot());
- this.reachedTarget = false;
- } else {
- this.reachedTarget = true;
- }
- }
- @Override
- protected boolean isValidTarget(LevelReader level, BlockPos pos) {
- return level.getBlockState(pos).is(Blocks.WATER) && level.getBlockState(pos.above()).isAir();
- }
- @Override
- protected boolean isReachedTarget() {
- return this.reachedTarget;
- }
- }
- public static class FloatingGoal extends Goal {
- private final SeaOtter seaOtter;
- private Phase phase = Phase.NONE;
- public FloatingGoal(SeaOtter mob) {
- this.seaOtter = mob;
- }
- @Override
- public boolean canUse() {
- return this.seaOtter.isInWater() && !this.seaOtter.isFloating() && this.seaOtter.getSurfacePos() != null && this.seaOtter.hasReachedTarget(this.seaOtter.getSurfacePos(), 0.25D);
- }
- @Override
- public boolean canContinueToUse() {
- return this.phase != Phase.NONE;
- }
- @Override
- public void start() {
- this.seaOtter.setFloating(true);
- this.seaOtter.switchToState(State.START_FLOATING);
- this.phase = Phase.START_FLOATING;
- }
- @Override
- public void stop() {
- this.seaOtter.setFloating(false);
- this.seaOtter.setSurfacePos(null);
- }
- @Override
- public void tick() {
- switch (this.phase) {
- case START_FLOATING -> {
- if (this.seaOtter.inStateTicks >= State.START_FLOATING.animationDuration()) {
- this.seaOtter.resetTicksSinceLastSwam();
- this.seaOtter.switchToState(State.FLOATING);
- this.seaOtter.setFloating(true);
- this.phase = Phase.FLOATING;
- }
- }
- case FLOATING -> {
- if (this.seaOtter.inStateTicks >= State.FLOATING.animationDuration() && this.seaOtter.wantsToSwim()) {
- this.seaOtter.switchToState(State.STOP_FLOATING);
- this.seaOtter.setFloating(true);
- this.phase = Phase.STOP_FLOATING;
- }
- }
- case STOP_FLOATING -> {
- if (this.seaOtter.inStateTicks >= State.STOP_FLOATING.animationDuration()) {
- this.seaOtter.switchToState(State.SWIMMING);
- this.seaOtter.setFloating(true);
- this.phase = Phase.NONE;
- }
- }
- }
- }
- enum Phase {
- NONE,
- START_FLOATING,
- FLOATING,
- STOP_FLOATING
- }
- }
- public static class SeaOtterMoveControl extends MoveControl {
- private final SeaOtter seaOtter;
- public SeaOtterMoveControl(SeaOtter mob) {
- super(mob);
- this.seaOtter = mob;
- }
- @Override
- public void tick() {
- if (this.seaOtter.isInWater()) {
- this.seaOtter.setDeltaMovement(this.seaOtter.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
- if (this.operation == MoveControl.Operation.MOVE_TO && !this.seaOtter.getNavigation().isDone()) {
- double d0 = this.wantedX - this.seaOtter.getX();
- double d1 = this.wantedY - this.seaOtter.getY();
- double d2 = this.wantedZ - this.seaOtter.getZ();
- double d3 = d0 * d0 + d1 * d1 + d2 * d2;
- if (d3 < (double)2.5000003E-7F) {
- this.seaOtter.setZza(0.0F);
- } else {
- float f = (float)(Mth.atan2(d2, d0) * (double)(180F / (float)Math.PI)) - 90.0F;
- this.seaOtter.setYRot(this.rotlerp(this.seaOtter.getYRot(), f, (float)10));
- this.seaOtter.yBodyRot = this.seaOtter.getYRot();
- this.seaOtter.yHeadRot = this.seaOtter.getYRot();
- float speed = (float)(this.speedModifier * this.seaOtter.getAttributeValue(Attributes.MOVEMENT_SPEED));
- this.seaOtter.setSpeed(speed * 0.02F);
- double d4 = Math.sqrt(d0 * d0 + d2 * d2);
- if (Math.abs(d1) > (double)1.0E-5F || Math.abs(d4) > (double)1.0E-5F) {
- float f3 = -((float)(Mth.atan2(d1, d4) * (double)(180F / (float)Math.PI)));
- f3 = Mth.clamp(Mth.wrapDegrees(f3), (float)(-85), (float)85);
- this.seaOtter.setXRot(this.rotlerp(this.seaOtter.getXRot(), f3, 5.0F));
- }
- if (d1 > (double)this.seaOtter.maxUpStep() && d0 * d0 + d2 * d2 < 4.0F && d1 <= 1.0D && this.seaOtter.level().getBlockState(BlockPos.containing(this.wantedX, this.wantedY, this.wantedZ)).getFluidState().isEmpty()) {
- this.seaOtter.getJumpControl().jump();
- this.seaOtter.setSpeed(speed);
- }
- float f6 = Mth.cos(this.seaOtter.getXRot() * ((float)Math.PI / 180F));
- float f4 = Mth.sin(this.seaOtter.getXRot() * ((float)Math.PI / 180F));
- this.seaOtter.zza = f6 * speed;
- this.seaOtter.yya = -f4 * speed;
- }
- } else {
- this.seaOtter.setSpeed(0.0F);
- this.seaOtter.setXxa(0.0F);
- this.seaOtter.setYya(0.0F);
- this.seaOtter.setZza(0.0F);
- }
- } else {
- if (this.hasWanted() && this.seaOtter.isSitting()) {
- this.seaOtter.standUp();
- }
- if (!this.seaOtter.isFloating()) {
- super.tick();
- }
- }
- }
- }
- public static class SeaOtterLookControl extends LookControl {
- private final SeaOtter seaOtter;
- public SeaOtterLookControl(SeaOtter mob) {
- super(mob);
- this.seaOtter = mob;
- }
- @Override
- protected Optional<Float> getXRotD() {
- if (this.seaOtter.isFloating() && super.getXRotD().isPresent()) {
- return Optional.of(-super.getXRotD().get());
- }
- return super.getXRotD();
- }
- @Override
- protected Optional<Float> getYRotD() {
- if (this.seaOtter.isFloating() && super.getYRotD().isPresent()) {
- return Optional.of(Mth.wrapDegrees(super.getYRotD().get() + 180.0F));
- }
- return super.getYRotD();
- }
- @Override
- protected boolean resetXRotOnTick() {
- boolean flag = this.seaOtter.getState() == State.START_FLOATING && this.seaOtter.inStateTicks < State.START_FLOATING.animationDuration();
- boolean flag2 = this.seaOtter.getState() == State.STOP_FLOATING && this.seaOtter.inStateTicks < State.STOP_FLOATING.animationDuration();
- return flag || flag2;
- }
- @Override
- public void tick() {
- if (this.resetXRotOnTick()) {
- this.mob.setXRot(0.0F);
- }
- if ((this.seaOtter.getState() == State.START_FLOATING && this.seaOtter.inStateTicks >= State.START_FLOATING.animationDuration()) && (this.seaOtter.getState() == State.STOP_FLOATING && this.seaOtter.inStateTicks >= State.STOP_FLOATING.animationDuration())) {
- if (this.seaOtter.isInWater()) {
- if (this.lookAtCooldown > 0) {
- --this.lookAtCooldown;
- this.getYRotD().ifPresent((i) -> this.seaOtter.yHeadRot = this.rotateTowards(this.seaOtter.yHeadRot, i + 20.0F, this.yMaxRotSpeed));
- this.getXRotD().ifPresent((i) -> this.seaOtter.setXRot(this.rotateTowards(this.seaOtter.getXRot(), i + 10.0F, this.xMaxRotAngle)));
- } else {
- if (this.seaOtter.getNavigation().isDone()) {
- this.seaOtter.setXRot(this.rotateTowards(this.seaOtter.getXRot(), 0.0F, 5.0F));
- }
- this.seaOtter.yHeadRot = this.rotateTowards(this.seaOtter.yHeadRot, this.seaOtter.yBodyRot, this.yMaxRotSpeed);
- }
- } else {
- super.tick();
- }
- }
- }
- }
- public class SeaOtterPathNavigation extends WaterBoundPathNavigation {
- public SeaOtterPathNavigation(Mob mob, Level level) {
- super(mob, level);
- }
- @Override
- protected PathFinder createPathFinder(int nodes) {
- this.nodeEvaluator = new AmphibiousNodeEvaluator(true);
- return new PathFinder(this.nodeEvaluator, nodes);
- }
- @Override
- protected boolean canUpdatePath() {
- return true;
- }
- @Override
- public boolean isStableDestination(BlockPos pos) {
- BlockState belowState = this.level.getBlockState(pos.below());
- if (SeaOtter.this.isInWater()) {
- return !(belowState.isAir() || belowState.getFluidState().is(FluidTags.WATER));
- } else {
- return !belowState.isAir();
- }
- }
- }
- public class SeaOtterBodyRotationControl extends BodyRotationControl {
- public SeaOtterBodyRotationControl(Mob mob) {
- super(mob);
- }
- @Override
- public void clientTick() {
- if (!SeaOtter.this.refuseToMove() && !SeaOtter.this.isFloating()) {
- super.clientTick();
- }
- }
- }
- public enum State implements StringRepresentable {
- NOTHING("nothing", 0, 0),
- IDLE_IN_WATER("idle_in_water", 1, 50),
- SWIMMING("swimming", 2, 40),
- GLIDE_WHILE_SWIMMING("glide_while_swimming", 3, 40),
- TWIRL_WHILE_SWIMMING("twirl_while_swimming", 4, 30),
- FLOATING("floating", 5, 50),
- START_FLOATING("start_floating", 6, 25),
- STOP_FLOATING("stop_floating", 7, 40),
- SWIMMING_WHILE_FLOATING("swimming_while_floating", 8, 50),
- TWIRL_WHILE_FLOATING("twirl_while_floating", 9, 55),
- SIT_DOWN("sit_down", 10, 30),
- SITTING("sitting", 11, 60),
- STAND_UP("stand_up", 12, 40),
- WALKING("walking", 13, 45);
- private static final IntFunction<SeaOtter.State> BY_ID = ByIdMap.continuous(SeaOtter.State::id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
- public static final StreamCodec<ByteBuf, SeaOtter.State> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, SeaOtter.State::id);
- private final String name;
- private final int id;
- private final int animationDuration;
- State(final String name, final int id, final int animationDuration) {
- this.name = name;
- this.id = id;
- this.animationDuration = animationDuration;
- }
- public static SeaOtter.State fromName(String name) {
- return StringRepresentable.fromEnum(SeaOtter.State::values).byName(name, NOTHING);
- }
- @Override
- public String getSerializedName() {
- return this.name;
- }
- private int id() {
- return this.id;
- }
- public int animationDuration() {
- return this.animationDuration;
- }
- }
- private enum Action {
- IDLING,
- SWIMMING_TO_SURFACE,
- SWIMMING,
- WALKING_TO_WATER
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment