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.client.model.geom.ModelPart;
- import net.minecraft.client.model.geom.PartPose;
- import net.minecraft.core.BlockPos;
- import net.minecraft.nbt.CompoundTag;
- import net.minecraft.nbt.NbtUtils;
- 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.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.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.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.LevelReader;
- import net.minecraft.world.level.block.Blocks;
- import net.minecraft.world.level.block.state.BlockState;
- import net.minecraft.world.level.pathfinder.*;
- import net.minecraft.world.phys.Vec3;
- import javax.annotation.Nullable;
- import java.util.EnumSet;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Optional;
- import java.util.function.IntFunction;
- import java.util.function.Predicate;
- /**
- * 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.
- *
- * todo:
- * swim around
- * drift around
- * hold hands
- * breed
- * search for food
- *
- * sleeping
- * hold hands while sleeping
- * babies sleeping on parent
- * babies behavior
- * search for fish
- * search for seashells
- * break open shells
- * bring items to players
- * eat
- */
- public class SeaOtter extends Animal {
- public static final EntityDataSerializer<State> SEA_OTTER_STATE = EntityDataSerializer.forValueType(State.STREAM_CODEC);
- private static final EntityDataAccessor<State> DATA_STATE_ID = SynchedEntityData.defineId(SeaOtter.class, SEA_OTTER_STATE);
- private static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(SeaOtter.class, EntityDataSerializers.BYTE);
- private int ticksSinceLastSwam;
- private int ticksSpentSwimming;
- @Nullable
- private BlockPos surfacePos;
- public final Map<ModelPart, PartPose> previousPose = new HashMap<>();
- public final Map<ModelPart, PartPose> targetPose = new HashMap<>();
- public final AnimationState swimmingIdleAnimationState = new AnimationState();
- public final AnimationState swimmingAnimationState = new AnimationState();
- public final AnimationState twirlWhileSwimmingAnimationState = new AnimationState();
- public final AnimationState floatingAnimationState = new AnimationState();
- public final AnimationState startFloatingAnimationState = new AnimationState();
- public final AnimationState stopFloatingAnimationState = new AnimationState();
- public final AnimationState sleepingWhileFloatingAnimationState = new AnimationState();
- public final AnimationState eatingWhileFloatingAnimationState = new AnimationState();
- public final AnimationState sitDownAnimationState = new AnimationState();
- public final AnimationState sittingIdleAnimationState = new AnimationState();
- public final AnimationState standUpAnimationState = new AnimationState();
- public final AnimationState standingIdleAnimationState = 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 SeaOtter.SeaOtterLookAtPlayerGoal(this, 6.0F));
- this.goalSelector.addGoal(2, new SeaOtter.FloatingGoal(this));
- this.goalSelector.addGoal(3, new SeaOtter.SeaOtterTemptGoal(this, 0.2D, (stack) -> stack.is(ItemTags.FISHES)));
- this.goalSelector.addGoal(4, new SeaOtter.SwimAroundGoal(this, 1.0D, 3));
- this.goalSelector.addGoal(5, new SeaOtter.SeaOtterRandomLookAroundGoal(this));
- }
- @Override
- protected void defineSynchedData(SynchedEntityData.Builder builder) {
- super.defineSynchedData(builder);
- builder.define(DATA_STATE_ID, State.NOTHING);
- builder.define(DATA_FLAGS_ID, (byte)0);
- }
- @Override
- public void addAdditionalSaveData(CompoundTag tag) {
- super.addAdditionalSaveData(tag);
- tag.putString("State", this.getState().getSerializedName());
- tag.putBoolean("Floating", this.isFloating());
- tag.putBoolean("Swimming", this.isSwimming());
- tag.putBoolean("Sitting", this.isSitting());
- tag.putInt("TicksSinceLastSwam", this.ticksSinceLastSwam);
- tag.putInt("TicksSpentSwimming", this.ticksSpentSwimming);
- if (this.surfacePos != null) {
- tag.put("SurfacePos", NbtUtils.writeBlockPos(this.surfacePos));
- }
- }
- @Override
- public void readAdditionalSaveData(CompoundTag tag) {
- super.readAdditionalSaveData(tag);
- this.switchToState(State.fromName(tag.getString("State")));
- this.setFloating(tag.getBoolean("Floating"));
- this.setSwimming(tag.getBoolean("Swimming"));
- this.setSitting(tag.getBoolean("Sitting"));
- this.ticksSinceLastSwam = tag.getInt("TicksSinceLastSwam");
- this.ticksSpentSwimming = tag.getInt("TicksSpentSwimming");
- this.surfacePos = null;
- if (tag.contains("SurfacePos") && NbtUtils.readBlockPos(tag, "SurfacePos").isPresent()) {
- this.surfacePos = NbtUtils.readBlockPos(tag, "SurfacePos").get();
- }
- }
- @Override
- public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
- return MysticEntities.SEA_OTTER.get().create(level);
- }
- @Override
- public int getMaxAirSupply() {
- return 6000; /// 5 minutes
- }
- @Override
- public void tick() {
- super.tick();
- if (!this.level().isClientSide()) {
- if (this.isFloating()) {
- this.ticksSinceLastSwam++;
- this.wasTouchingWater = true;
- this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
- this.setSpeed(0.0F);
- this.setYya(0.0F);
- if (this.getAirSupply() < this.getMaxAirSupply()) {
- this.setAirSupply(this.increaseAirSupply(this.getAirSupply()));
- }
- }
- if (this.isUnderWater()) {
- if (!this.needsToSurface() && (this.getAirSupply() < 600 || !this.wantsToSwimAround() || this.hasBeenSwimmingLongEnough())) {
- this.setNeedsToSurface(true);
- }
- }
- }
- if (this.level().isClientSide()) {
- this.setupSwimmingAnimationStates();
- this.setupFloatingAnimationStates();
- this.setupSittingAnimationStates();
- }
- this.inStateTicks++;
- }
- private void setupSwimmingAnimationStates() {
- switch (this.getState()) {
- case SWIMMING_IDLE:
- this.swimmingIdleAnimationState.startIfStopped(this.tickCount);
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case SWIMMING:
- this.swimmingAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case TWIRL_WHILE_SWIMMING:
- this.twirlWhileSwimmingAnimationState.startIfStopped(this.tickCount);
- break;
- }
- }
- private void setupFloatingAnimationStates() {
- switch (this.getState()) {
- case START_FLOATING:
- this.startFloatingAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case FLOATING:
- this.floatingAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case STOP_FLOATING:
- this.stopFloatingAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case SLEEPING_WHILE_FLOATING:
- this.sleepingWhileFloatingAnimationState.startIfStopped(this.tickCount);
- break;
- case EATING_WHILE_FLOATING:
- this.eatingWhileFloatingAnimationState.startIfStopped(this.tickCount);
- break;
- }
- }
- private void setupSittingAnimationStates() {
- switch (this.getState()) {
- case SIT_DOWN:
- this.sitDownAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case SITTING_IDLE:
- this.sittingIdleAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case STAND_UP:
- this.standUpAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standingIdleAnimationState.stop();
- this.walkingAnimationState.stop();
- break;
- case WALKING:
- this.walkingAnimationState.startIfStopped(this.tickCount);
- this.swimmingIdleAnimationState.stop();
- this.swimmingAnimationState.stop();
- this.twirlWhileSwimmingAnimationState.stop();
- this.floatingAnimationState.stop();
- this.startFloatingAnimationState.stop();
- this.stopFloatingAnimationState.stop();
- this.sleepingWhileFloatingAnimationState.stop();
- this.eatingWhileFloatingAnimationState.stop();
- this.sitDownAnimationState.stop();
- this.sittingIdleAnimationState.stop();
- this.standUpAnimationState.stop();
- this.standingIdleAnimationState.stop();
- break;
- }
- }
- @Override
- public void aiStep() {
- super.aiStep();
- 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_IDLE);
- 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 State getState() {
- return this.entityData.get(DATA_STATE_ID);
- }
- public void switchToState(State state) {
- if (this.getState() != state) {
- this.entityData.set(DATA_STATE_ID, state);
- this.inStateTicks = 0L;
- }
- }
- public boolean isActivelyInFloatTransition() {
- return switch (this.getState()) {
- case START_FLOATING, STOP_FLOATING, FLOATING -> this.inStateTicks < this.getState().animationDuration();
- default -> false;
- };
- }
- public boolean isActivelySittingTransition() {
- return switch (this.getState()) {
- case SIT_DOWN, STAND_UP -> this.inStateTicks < this.getState().animationDuration();
- default -> false;
- };
- }
- private boolean getFlag(int id) {
- return (this.entityData.get(DATA_FLAGS_ID) & id) != 0;
- }
- private void setFlag(int id, boolean value) {
- byte flag = this.entityData.get(DATA_FLAGS_ID);
- this.entityData.set(DATA_FLAGS_ID, value ? (byte)(flag | id) : (byte)(flag & ~id));
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public boolean isFloating() {
- return this.getFlag(1 << 1);
- }
- public void setFloating(boolean value) {
- this.setFlag(1 << 1, value);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public boolean isSwimming() {
- return this.getFlag(1 << 2);
- }
- public void setSwimming(boolean value) {
- this.setFlag(1 << 2, value);
- }
- /// once it's been long enough since it last swam around the sea otter will stop floating and start swimming.
- public boolean wantsToSwimAround() {
- return this.hasBeenLongEnoughSinceLastSwam();
- }
- public boolean hasBeenLongEnoughSinceLastSwam() {
- return this.ticksSinceLastSwam >= 400;
- }
- public void resetTicksSinceLastSwam() {
- this.ticksSinceLastSwam = 0;
- }
- public boolean hasBeenSwimmingLongEnough() {
- return this.ticksSpentSwimming >= 200;
- }
- public void resetTicksSpentSwimming() {
- this.ticksSpentSwimming = 0;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public boolean isSitting() {
- return this.getFlag(1 << 3);
- }
- public void setSitting(boolean value) {
- this.setFlag(1 << 3, value);
- }
- public void sitDown() {
- if (!this.isSitting() && this.onGround() && !this.isInWater()) {
- this.setSitting(true);
- this.switchToState(State.SIT_DOWN);
- }
- }
- public void standUp() {
- if (this.isSitting()) {
- this.setSitting(false);
- this.switchToState(State.STAND_UP);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @Nullable
- public BlockPos getSurfacePos() {
- return this.surfacePos;
- }
- public void setSurfacePos(BlockPos pos) {
- this.surfacePos = pos;
- }
- public boolean needsToSurface() {
- return this.getFlag(1 << 4);
- }
- public void setNeedsToSurface(boolean value) {
- this.setFlag(1 << 4, value);
- }
- public boolean hasReachedSurface() {
- return this.getFlag(1 << 5);
- }
- public void setReachedSurface(boolean value) {
- this.setFlag(1 << 5, value);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @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.setDeltaMovement(this.getDeltaMovement().multiply(0.0, 1.0, 0.0));
- pos = pos.multiply(0.0, 1.0, 0.0);
- }
- super.travel(pos);
- }
- public void pathfindDirectlyTowards(BlockPos pos, double speed) {
- this.navigation.setMaxVisitedNodesMultiplier(10.0F);
- this.navigation.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, speed);
- }
- public boolean refuseToMove() {
- return (this.isSitting() || this.isActivelySittingTransition()) || (this.getState() == State.FLOATING);
- }
- public boolean wantsToMove() {
- return !this.getNavigation().isDone() || this.getTarget() != null || this.moveControl.hasWanted();
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @Override
- public Vec3 getViewVector(float ticks) {
- Vec3 view = super.getViewVector(ticks);
- if (this.isFloating()) {
- return view.reverse();
- }
- return view;
- }
- @Override
- public int getMaxHeadYRot() {
- if (this.isFloating()) {
- return 2;
- } else if (this.isSitting()) {
- return 20;
- } else {
- return super.getMaxHeadYRot();
- }
- }
- @Override
- public int getMaxHeadXRot() {
- if (this.isFloating()) {
- return 20;
- } else if (this.isSitting()) {
- return 20;
- } else {
- return super.getMaxHeadXRot();
- }
- }
- @Override
- public boolean isFood(ItemStack stack) {
- return stack.is(ItemTags.FISHES);
- }
- @Override
- public InteractionResult mobInteract(Player player, InteractionHand hand) {
- if (!this.level().isClientSide()) {
- if (player.getItemInHand(hand).isEmpty()) {
- this.setSitting(false);
- this.setFloating(true);
- this.switchToState(State.START_FLOATING);
- System.out.println("ticksSinceLastSwam: " + this.ticksSinceLastSwam);
- System.out.println("ticksSpentSwimming: " + this.ticksSpentSwimming);
- }
- }
- return super.mobInteract(player, hand);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public static class SwimToSurfaceGoal extends Goal {
- private final SeaOtter seaOtter;
- @Nullable
- private BlockPos blockPos;
- public SwimToSurfaceGoal(SeaOtter mob) {
- this.seaOtter = mob;
- this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
- }
- @Override
- public boolean canUse() {
- if (this.seaOtter.isFloating()) {
- return false;
- } else if (this.seaOtter.hasBeenLongEnoughSinceLastSwam()) { /// meaning the sea otter should be swimming.
- return false;
- }
- return this.seaOtter.needsToSurface();
- }
- @Override
- public boolean canContinueToUse() {
- return !this.seaOtter.hasReachedSurface();
- }
- @Override
- public void start() {
- if (this.findNearestPos().isPresent()) {
- this.blockPos = this.findNearestPos().get();
- this.seaOtter.setSurfacePos(this.blockPos);
- System.out.println("swimming to surface at : " + this.seaOtter.getSurfacePos());
- }
- }
- @Override
- public void stop() {
- this.seaOtter.setNeedsToSurface(false);
- this.seaOtter.getNavigation().stop();
- System.out.println("reached surface");
- }
- @Override
- public void tick() {
- this.seaOtter.setReachedSurface(false);
- if (this.blockPos == null) {
- if (this.findNearestPos().isPresent()) {
- this.blockPos = this.findNearestPos().get();
- }
- } else {
- this.seaOtter.pathfindDirectlyTowards(this.blockPos, 2.5F);
- this.seaOtter.getLookControl().setLookAt(Vec3.atLowerCornerOf(this.blockPos));
- double distanceToSurface = this.blockPos.getY() - this.seaOtter.getEyePosition().y();
- if (this.seaOtter.getEyeY() < this.blockPos.getY() && this.blockPos.distToCenterSqr(this.seaOtter.position()) <= 5.0D) {
- this.seaOtter.push(0.0D, Math.min(0.03D, distanceToSurface * 0.05D), 0.0D);
- this.seaOtter.setSpeed(2.5F * (float) Math.max(0.3D, Math.min(1.0D, distanceToSurface)));
- }
- if (Math.abs(distanceToSurface) <= 0.2D) {
- this.seaOtter.setDeltaMovement(Vec3.ZERO);
- this.seaOtter.setSpeed(0.0F);
- this.seaOtter.setYya(0.0F);
- /// set that the sea otter has reached the surface, ending the goal and starting the float goal.
- this.seaOtter.setReachedSurface(true);
- }
- }
- }
- protected Optional<BlockPos> findNearestPos() {
- BlockPos pos = this.seaOtter.blockPosition();
- BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
- for (int i = 0; (double)i <= 24; i = i > 0 ? -i : 1 - i) {
- for (int j = 0; (double)j < 24; ++j) {
- for (int k = 0; k <= j; k = k > 0 ? -k : 1 - k) {
- for (int l = k < j && k > -j ? j : 0; l <= j; l = l > 0 ? -l : 1 - l) {
- mutablePos.setWithOffset(pos, k, i - 1, l);
- if (this.seaOtter.isWithinRestriction(mutablePos) && this.isValidTarget(this.seaOtter.level(), mutablePos)) {
- return Optional.of(mutablePos);
- }
- }
- }
- }
- }
- return Optional.empty();
- }
- protected boolean isValidTarget(LevelReader level, BlockPos pos) {
- return level.getBlockState(pos.below()).is(Blocks.WATER) && level.getBlockState(pos).isAir();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public static class FloatingGoal extends Goal {
- private final SeaOtter seaOtter;
- private Phase phase;
- public FloatingGoal(SeaOtter mob) {
- this.seaOtter = mob;
- this.setFlags(EnumSet.of(Flag.MOVE));
- }
- @Override
- public boolean canUse() {
- return this.seaOtter.hasReachedSurface() && !this.seaOtter.wantsToSwimAround();
- }
- @Override
- public boolean canContinueToUse() {
- return this.phase != Phase.SWIMMING;
- }
- @Override
- public void start() {
- if (!this.seaOtter.isFloating()) {
- this.seaOtter.setReachedSurface(false);
- this.seaOtter.resetTicksSinceLastSwam();
- this.seaOtter.setFloating(true);
- this.seaOtter.switchToState(State.START_FLOATING);
- this.phase = Phase.START_FLOATING;
- System.out.println("started floating");
- }
- }
- @Override
- public void stop() {
- this.seaOtter.setReachedSurface(false);
- this.seaOtter.setNeedsToSurface(false);
- this.seaOtter.setFloating(false);
- this.seaOtter.switchToState(State.SWIMMING);
- System.out.println("finished floating");
- }
- @Override
- public void tick() {
- switch (this.phase) {
- case START_FLOATING -> {
- if (this.seaOtter.inStateTicks >= State.START_FLOATING.animationDuration()) {
- this.seaOtter.switchToState(State.FLOATING);
- this.phase = Phase.FLOATING;
- }
- }
- case FLOATING -> {
- if (this.seaOtter.inStateTicks >= State.FLOATING.animationDuration() && this.wantsToBeDone()) {
- System.out.println("stopping floating");
- this.seaOtter.switchToState(State.STOP_FLOATING);
- this.phase = Phase.STOP_FLOATING;
- }
- }
- case STOP_FLOATING -> {
- if (this.seaOtter.inStateTicks >= State.STOP_FLOATING.animationDuration()) {
- this.seaOtter.switchToState(State.SWIMMING);
- this.phase = Phase.SWIMMING;
- }
- }
- }
- }
- private boolean wantsToBeDone() {
- return this.seaOtter.wantsToSwimAround();
- }
- enum Phase {
- SWIMMING,
- START_FLOATING,
- FLOATING,
- STOP_FLOATING
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public static class SwimAroundGoal extends RandomSwimmingGoal {
- private final SeaOtter seaOtter;
- public SwimAroundGoal(SeaOtter mob, double speed, int interval) {
- super(mob, speed, interval);
- this.seaOtter = mob;
- this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
- }
- @Override
- public boolean canUse() {
- if (this.seaOtter.needsToSurface()) {
- return false;
- } else if (this.seaOtter.isFloating()) {
- return false;
- }
- return super.canUse();
- }
- @Override
- public boolean canContinueToUse() {
- if (this.seaOtter.needsToSurface()) {
- return false;
- } else if (this.seaOtter.hasBeenSwimmingLongEnough()) {
- return false;
- }
- return super.canContinueToUse();
- }
- @Override
- public void start() {
- super.start();
- this.seaOtter.setSwimming(true);
- System.out.println("started swimming");
- }
- @Override
- public void stop() {
- super.stop();
- if (this.seaOtter.hasBeenSwimmingLongEnough()) {
- this.seaOtter.setNeedsToSurface(true);
- this.seaOtter.resetTicksSinceLastSwam();
- this.seaOtter.resetTicksSpentSwimming();
- System.out.println("stopped swimming");
- }
- }
- @Override
- public void tick() {
- this.seaOtter.ticksSpentSwimming++;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public static class SeaOtterLookAtPlayerGoal extends LookAtPlayerGoal {
- private final SeaOtter seaOtter;
- public SeaOtterLookAtPlayerGoal(SeaOtter mob, float distance) {
- super(mob, Player.class, distance);
- this.seaOtter = mob;
- }
- @Override
- public boolean canUse() {
- return super.canUse() && !this.seaOtter.isSwimming() && !this.seaOtter.isActivelyInFloatTransition();
- }
- }
- public static class SeaOtterTemptGoal extends TemptGoal {
- private final SeaOtter seaOtter;
- public SeaOtterTemptGoal(SeaOtter mob, double speed, Predicate<ItemStack> items) {
- super(mob, speed, items, false);
- this.seaOtter = mob;
- }
- }
- public static class SeaOtterRandomLookAroundGoal extends RandomLookAroundGoal {
- private final SeaOtter seaOtter;
- public SeaOtterRandomLookAroundGoal(SeaOtter mob) {
- super(mob);
- this.seaOtter = mob;
- }
- @Override
- public boolean canUse() {
- return super.canUse() && !this.seaOtter.isSwimming();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- 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.isSleeping()) return;
- if (this.seaOtter.isInWater() && !this.seaOtter.isFloating()) {
- this.seaOtter.setDeltaMovement(this.seaOtter.getDeltaMovement().add(this.seaOtter.getLookAngle().scale(this.seaOtter.isFloating() ? 0.002F : 0.005F)));
- 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 > 0.0D && 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() + 160.0F));
- }
- return super.getYRotD();
- }
- @Override
- protected boolean resetXRotOnTick() {
- return this.seaOtter.isActivelyInFloatTransition();
- }
- @Override
- public void tick() {
- if (this.resetXRotOnTick()) {
- this.seaOtter.setXRot(0.0F);
- return;
- }
- if (this.seaOtter.isInWater() && this.seaOtter.isUnderWater()) {
- 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);
- }
- float f = Mth.wrapDegrees(this.seaOtter.yHeadRot - this.seaOtter.yBodyRot);
- if (f < (float)(-10)) {
- this.seaOtter.yBodyRot -= 4.0F;
- } else if (f > (float)10) {
- this.seaOtter.yBodyRot += 4.0F;
- }
- } else {
- super.tick();
- }
- }
- }
- public static class SeaOtterBodyRotationControl extends BodyRotationControl {
- private final SeaOtter seaOtter;
- public SeaOtterBodyRotationControl(SeaOtter mob) {
- super(mob);
- this.seaOtter = mob;
- }
- @Override
- public void clientTick() {
- if (!this.seaOtter.refuseToMove() && !this.seaOtter.isFloating()) {
- super.clientTick();
- }
- }
- }
- public static class SeaOtterPathNavigation extends AmphibiousPathNavigation {
- private final SeaOtter seaOtter;
- public SeaOtterPathNavigation(SeaOtter mob, Level level) {
- super(mob, level);
- this.seaOtter = mob;
- this.setCanFloat(true);
- }
- @Override
- protected PathFinder createPathFinder(int nodes) {
- this.nodeEvaluator = new AmphibiousNodeEvaluator(true);
- return new PathFinder(this.nodeEvaluator, nodes);
- }
- @Override
- public boolean isStableDestination(BlockPos pos) {
- BlockState belowState = this.level.getBlockState(pos.below());
- if (this.seaOtter.isInWater()) {
- return !(belowState.isAir() || belowState.getFluidState().is(FluidTags.WATER));
- } else {
- return !belowState.isAir();
- }
- }
- }
- public enum State implements StringRepresentable {
- NOTHING("nothing", 0, 0),
- SWIMMING_IDLE("swimming_idle", 1, 50),
- SWIMMING("swimming", 2, 40),
- TWIRL_WHILE_SWIMMING("twirl_while_swimming", 3, 30),
- FLOATING("floating", 4, 50),
- START_FLOATING("start_floating", 5, 30),
- STOP_FLOATING("stop_floating", 6, 25),
- SLEEPING_WHILE_FLOATING("sleeping_while_floating", 7, 0),
- EATING_WHILE_FLOATING("eating_while_floating", 8, 50),
- SIT_DOWN("sit_down", 9, 30),
- SITTING_IDLE("sitting_idle", 10, 60),
- STAND_UP("stand_up", 11, 30),
- STANDING_IDLE("standing_idle", 12, 60),
- 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;
- }
- }
- /**
- * todo: Current Actions
- *
- * actions decide what the entity wants to do.
- * - i.e. a sea otter who wants to swim, if they're currently floating, once that is finished they will start swimming.
- *
- * que actions so when the current one is finished they will start the next one.
- *
- * actions will be paired with a reasoning.
- * - i.e. the action "wants to swim" can have the reasoning for why it's doing that, being able to control what "wants to swim" will do exactly.
- *
- * each action has a base length, so like they can only start the next one if they're done with their current one. this is overridden in the case of
- * an emergency, like if they need to run away, or are gonna drown and need to stop looking for fish.
- */
- private enum CoreStates {
- ON_LAND,
- FLOATING_ON_SURFACE,
- SURFACE_SWIMMING,
- DEEP_SEA_SWIMMING
- }
- private enum ActionReasons {
- NEEDS_AIR,
- WANTS_TO_FLOAT,
- IS_BEDTIME
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment