jayhillx

Sea Otter 5

Aug 7th, 2025
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 35.98 KB | None | 0 0
  1. package com.mysticsbiomes.common.entity;
  2.  
  3. import com.mysticsbiomes.init.MysticEntities;
  4. import io.netty.buffer.ByteBuf;
  5. import net.minecraft.core.BlockPos;
  6. import net.minecraft.nbt.CompoundTag;
  7. import net.minecraft.network.codec.ByteBufCodecs;
  8. import net.minecraft.network.codec.StreamCodec;
  9. import net.minecraft.network.syncher.EntityDataAccessor;
  10. import net.minecraft.network.syncher.EntityDataSerializer;
  11. import net.minecraft.network.syncher.EntityDataSerializers;
  12. import net.minecraft.network.syncher.SynchedEntityData;
  13. import net.minecraft.server.level.ServerLevel;
  14. import net.minecraft.tags.FluidTags;
  15. import net.minecraft.tags.ItemTags;
  16. import net.minecraft.util.ByIdMap;
  17. import net.minecraft.util.Mth;
  18. import net.minecraft.util.StringRepresentable;
  19. import net.minecraft.world.DifficultyInstance;
  20. import net.minecraft.world.InteractionHand;
  21. import net.minecraft.world.InteractionResult;
  22. import net.minecraft.world.entity.*;
  23. import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
  24. import net.minecraft.world.entity.ai.attributes.Attributes;
  25. import net.minecraft.world.entity.ai.behavior.BehaviorUtils;
  26. import net.minecraft.world.entity.ai.control.BodyRotationControl;
  27. import net.minecraft.world.entity.ai.control.LookControl;
  28. import net.minecraft.world.entity.ai.control.MoveControl;
  29. import net.minecraft.world.entity.ai.goal.*;
  30. import net.minecraft.world.entity.ai.navigation.PathNavigation;
  31. import net.minecraft.world.entity.ai.navigation.WaterBoundPathNavigation;
  32. import net.minecraft.world.entity.animal.Animal;
  33. import net.minecraft.world.entity.player.Player;
  34. import net.minecraft.world.item.ItemStack;
  35. import net.minecraft.world.level.Level;
  36. import net.minecraft.world.level.LevelReader;
  37. import net.minecraft.world.level.ServerLevelAccessor;
  38. import net.minecraft.world.level.block.Blocks;
  39. import net.minecraft.world.level.block.state.BlockState;
  40. import net.minecraft.world.level.pathfinder.AmphibiousNodeEvaluator;
  41. import net.minecraft.world.level.pathfinder.Path;
  42. import net.minecraft.world.level.pathfinder.PathFinder;
  43. import net.minecraft.world.level.pathfinder.PathType;
  44. import net.minecraft.world.phys.Vec3;
  45.  
  46. import javax.annotation.Nullable;
  47. import java.util.Optional;
  48. import java.util.function.IntFunction;
  49.  
  50. /**
  51.  * can be tamed as pets to defend you in the water.
  52.  * they can help you find treasures, or collect fish.
  53.  * they can help you swim faster like dolphins.
  54.  * they mostly float around, unless they are feeling playful or want to get food.
  55.  * they will fall asleep at night, holding another sea otters hand nearby.
  56.  * their babies will rest on their belly until they age up.
  57.  */
  58. public class SeaOtter extends Animal {
  59.     public static final EntityDataSerializer<SeaOtter.State> SEA_OTTER_STATE = EntityDataSerializer.forValueType(SeaOtter.State.STREAM_CODEC);
  60.     private static final EntityDataAccessor<SeaOtter.State> DATA_STATE_ID = SynchedEntityData.defineId(SeaOtter.class, SEA_OTTER_STATE);
  61.     private static final EntityDataAccessor<Boolean> DATA_FLOATING_ID = SynchedEntityData.defineId(SeaOtter.class, EntityDataSerializers.BOOLEAN);
  62.     private static final EntityDataAccessor<Boolean> DATA_SITTING_ID = SynchedEntityData.defineId(SeaOtter.class, EntityDataSerializers.BOOLEAN);
  63.     private boolean needsToSurface;
  64.     private int ticksSinceLastSwamAround;
  65.     private int ticksOutOfWater;
  66.     @Nullable
  67.     private BlockPos surfacePos;
  68.     public final AnimationState idleInWaterAnimationState = new AnimationState();
  69.     public final AnimationState swimmingAnimationState = new AnimationState();
  70.     public final AnimationState glideWhileSwimmingAnimationState = new AnimationState();
  71.     public final AnimationState twirlWhileSwimmingAnimationState = new AnimationState();
  72.     public final AnimationState floatingAnimationState = new AnimationState();
  73.     public final AnimationState swimmingWhileFloatingAnimationState = new AnimationState();
  74.     public final AnimationState twirlWhileFloatingAnimationState = new AnimationState();
  75.     public final AnimationState startFloatingAnimationState = new AnimationState();
  76.     public final AnimationState stopFloatingAnimationState = new AnimationState();
  77.     public final AnimationState sitDownAnimationState = new AnimationState();
  78.     public final AnimationState sittingAnimationState = new AnimationState();
  79.     public final AnimationState standUpAnimationState = new AnimationState();
  80.     public final AnimationState walkingAnimationState = new AnimationState();
  81.     private long inStateTicks = 0L;
  82.  
  83.     public SeaOtter(EntityType<? extends SeaOtter> type, Level level) {
  84.         super(type, level);
  85.         this.setPathfindingMalus(PathType.WATER, 0.0F);
  86.         this.moveControl = new SeaOtter.SeaOtterMoveControl(this);
  87.         this.lookControl = new SeaOtter.SeaOtterLookControl(this);
  88.     }
  89.  
  90.     public static AttributeSupplier.Builder createAttributes() {
  91.         return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 16.0F).add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.STEP_HEIGHT, 1.0);
  92.     }
  93.  
  94.     @Override
  95.     protected void registerGoals() {
  96.         this.goalSelector.addGoal(0, new SeaOtter.SwimToSurfaceGoal(this));
  97.         this.goalSelector.addGoal(1, new TemptGoal(this, 0.2D, (stack) -> stack.is(ItemTags.FISHES), false));
  98.         this.goalSelector.addGoal(2, new LookAtPlayerGoal(this, Player.class, 6.0F));
  99.         this.goalSelector.addGoal(3, new RandomLookAroundGoal(this));
  100.         this.goalSelector.addGoal(4, new SeaOtter.FloatingGoal(this));
  101.         this.goalSelector.addGoal(5, new SeaOtter.MoveToWaterGoal(this, 1.0D));
  102.         this.goalSelector.addGoal(6, new SeaOtter.SwimAroundGoal(this, 1.0D, 10));
  103.     }
  104.  
  105.     @Override
  106.     protected void defineSynchedData(SynchedEntityData.Builder builder) {
  107.         super.defineSynchedData(builder);
  108.         builder.define(DATA_STATE_ID, SeaOtter.State.NOTHING);
  109.         builder.define(DATA_FLOATING_ID, false);
  110.         builder.define(DATA_SITTING_ID, false);
  111.     }
  112.  
  113.     @Override
  114.     public void addAdditionalSaveData(CompoundTag tag) {
  115.         super.addAdditionalSaveData(tag);
  116.         tag.putString("State", this.getState().getSerializedName());
  117.         tag.putBoolean("Floating", this.isFloating());
  118.         tag.putBoolean("Sitting", this.isSitting());
  119.         tag.putBoolean("NeedsToSurface", this.needsToSurface());
  120.         tag.putInt("TicksSinceSwam", this.ticksSinceLastSwamAround);
  121.         tag.putInt("TicksOutOfWater", this.ticksOutOfWater);
  122.     }
  123.  
  124.     @Override
  125.     public void readAdditionalSaveData(CompoundTag tag) {
  126.         super.readAdditionalSaveData(tag);
  127.         this.switchToState(SeaOtter.State.fromName(tag.getString("State")));
  128.         this.setFloating(tag.getBoolean("Floating"));
  129.         this.setSitting(tag.getBoolean("Sitting"));
  130.         this.setNeedsToSurface(tag.getBoolean("NeedsToSurface"));
  131.         this.ticksSinceLastSwamAround = tag.getInt("TicksSinceSwam");
  132.         this.ticksOutOfWater = tag.getInt("TicksOutOfWater");
  133.     }
  134.  
  135.     @Override
  136.     public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
  137.         return MysticEntities.SEA_OTTER.get().create(level);
  138.     }
  139.  
  140.     @Override
  141.     public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance instance, MobSpawnType type, SpawnGroupData data) {
  142.         return super.finalizeSpawn(level, instance, type, data);
  143.     }
  144.  
  145.     @Override
  146.     public int getMaxAirSupply() {
  147.         return 6000; /// 5 minutes
  148.     }
  149.  
  150.     @Override
  151.     public void tick() {
  152.         super.tick();
  153.         if (this.isFloating()) {
  154.             this.wasTouchingWater = true;
  155.             this.setAirSupply(this.getMaxAirSupply());
  156.             this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
  157.         }
  158.  
  159.         if (!this.level().isClientSide) {
  160.             if (this.isFloating()) {
  161.                 ++this.ticksSinceLastSwamAround;
  162.             }
  163.  
  164.             if (!this.isFloating() && !this.isInWater()) {
  165.                 ++this.ticksOutOfWater;
  166.             }
  167.  
  168.             if (this.tickCount % 60 == 0) {
  169.                 System.out.println("state: " + this.getState() + " isFloating: " + this.isFloating() + " isSitting: " + this.isSitting() + " ticksSinceLastSwamAround: " + this.ticksSinceLastSwamAround + " wantsToSwim: " + this.wantsToSwim());
  170.             }
  171.         }
  172.  
  173.         if (this.level().isClientSide()) {
  174.             this.setupSwimmingAnimationStates();
  175.             this.setupFloatingAnimationStates();
  176.             this.setupSittingAnimationStates();
  177.         }
  178.         this.inStateTicks++;
  179.     }
  180.  
  181.     private void setupSwimmingAnimationStates() {
  182.         switch (this.getState()) {
  183.             case IDLE_IN_WATER:
  184.                 this.swimmingAnimationState.stop();
  185.                 this.stopFloatingAnimationState.stop();
  186.                 this.walkingAnimationState.stop();
  187.                 this.sittingAnimationState.stop();
  188.                 this.standUpAnimationState.stop();
  189.                 this.sitDownAnimationState.stop();
  190.                 this.idleInWaterAnimationState.startIfStopped(this.tickCount);
  191.                 break;
  192.             case SWIMMING:
  193.                 this.idleInWaterAnimationState.stop();
  194.                 this.stopFloatingAnimationState.stop();
  195.                 this.walkingAnimationState.stop();
  196.                 this.sittingAnimationState.stop();
  197.                 this.standUpAnimationState.stop();
  198.                 this.sitDownAnimationState.stop();
  199.                 this.swimmingAnimationState.startIfStopped(this.tickCount);
  200.                 break;
  201.             case GLIDE_WHILE_SWIMMING:
  202.                 this.swimmingAnimationState.stop();
  203.                 this.glideWhileSwimmingAnimationState.startIfStopped(this.tickCount);
  204.                 break;
  205.             case TWIRL_WHILE_SWIMMING:
  206.                 this.twirlWhileSwimmingAnimationState.startIfStopped(this.tickCount);
  207.                 break;
  208.         }
  209.     }
  210.  
  211.     private void setupFloatingAnimationStates() {
  212.         switch (this.getState()) {
  213.             case START_FLOATING:
  214.                 this.idleInWaterAnimationState.stop();
  215.                 this.swimmingAnimationState.stop();
  216.                 this.stopFloatingAnimationState.stop();
  217.                 this.floatingAnimationState.stop();
  218.                 this.startFloatingAnimationState.startIfStopped(this.tickCount);
  219.                 break;
  220.             case FLOATING:
  221.                 this.idleInWaterAnimationState.stop();
  222.                 this.swimmingAnimationState.stop();
  223.                 this.startFloatingAnimationState.stop();
  224.                 this.stopFloatingAnimationState.stop();
  225.                 this.floatingAnimationState.startIfStopped(this.tickCount);
  226.                 break;
  227.             case STOP_FLOATING:
  228.                 this.idleInWaterAnimationState.stop();
  229.                 this.swimmingAnimationState.stop();
  230.                 this.floatingAnimationState.stop();
  231.                 this.startFloatingAnimationState.stop();
  232.                 this.stopFloatingAnimationState.startIfStopped(this.tickCount);
  233.                 break;
  234.             case SWIMMING_WHILE_FLOATING:
  235.                 this.swimmingWhileFloatingAnimationState.startIfStopped(this.tickCount);
  236.                 break;
  237.             case TWIRL_WHILE_FLOATING:
  238.                 this.twirlWhileFloatingAnimationState.startIfStopped(this.tickCount);
  239.                 break;
  240.         }
  241.     }
  242.  
  243.     private void setupSittingAnimationStates() {
  244.         switch (this.getState()) {
  245.             case SIT_DOWN:
  246.                 this.standUpAnimationState.stop();
  247.                 this.sittingAnimationState.stop();
  248.                 this.walkingAnimationState.stop();
  249.                 this.sitDownAnimationState.startIfStopped(this.tickCount);
  250.                 break;
  251.             case SITTING:
  252.                 this.standUpAnimationState.stop();
  253.                 this.walkingAnimationState.stop();
  254.                 this.sitDownAnimationState.stop();
  255.                 this.sittingAnimationState.startIfStopped(this.tickCount);
  256.                 break;
  257.             case STAND_UP:
  258.                 this.sittingAnimationState.stop();
  259.                 this.walkingAnimationState.stop();
  260.                 this.sitDownAnimationState.stop();
  261.                 this.standUpAnimationState.startIfStopped(this.tickCount);
  262.                 break;
  263.             case WALKING:
  264.                 this.sitDownAnimationState.stop();
  265.                 this.sittingAnimationState.stop();
  266.                 this.standUpAnimationState.stop();
  267.                 this.walkingAnimationState.startIfStopped(this.tickCount);
  268.                 break;
  269.         }
  270.     }
  271.  
  272.     @Override
  273.     public void aiStep() {
  274.         super.aiStep();
  275.         if (this.isEffectiveAi() && this.isAlive()) {
  276.             if (!this.needsToSurface() && this.isUnderWater() && (!this.wantsToSwim() || this.getAirSupply() < 400)) {
  277.                 this.setNeedsToSurface(true);
  278.                 System.out.println("needs to surface");
  279.             }
  280.         }
  281.  
  282.         if (!this.level().isClientSide()) {
  283.             if (this.onGround() && !this.isInWater() && !this.isFloating()) {
  284.                 if (this.wantsToMove()) {
  285.                     if (this.isSitting() || this.getState() == State.SIT_DOWN) {
  286.                         if (this.getState() != State.STAND_UP) {
  287.                             this.switchToState(State.STAND_UP);
  288.                             this.setSitting(false);
  289.                         }
  290.                     }
  291.  
  292.                     if (this.getState() == State.STAND_UP && this.inStateTicks >= State.STAND_UP.animationDuration()) {
  293.                         this.switchToState(State.WALKING);
  294.                     }
  295.  
  296.                     if (!this.isSitting() && this.getState() != State.STAND_UP && this.getState() != State.WALKING) {
  297.                         this.switchToState(State.WALKING);
  298.                     }
  299.                 } else {
  300.                     if (!this.isSitting() && this.getState() != State.SIT_DOWN) {
  301.                         this.switchToState(State.SIT_DOWN);
  302.                         this.setSitting(true);
  303.                     } else if (this.getState() == State.SIT_DOWN && this.inStateTicks >= State.SIT_DOWN.animationDuration()) {
  304.                         this.switchToState(State.SITTING);
  305.                         this.setSitting(true);
  306.                     }
  307.  
  308.                     if (this.getState() == State.STAND_UP && this.inStateTicks >= State.STAND_UP.animationDuration()) {
  309.                         this.switchToState(State.SIT_DOWN);
  310.                         this.setSitting(true);
  311.                     }
  312.                 }
  313.             }
  314.         }
  315.     }
  316.  
  317.     @Override
  318.     public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
  319.         if (DATA_STATE_ID.equals(accessor)) {
  320.             this.inStateTicks = 0L;
  321.         }
  322.  
  323.         super.onSyncedDataUpdated(accessor);
  324.     }
  325.  
  326.     public SeaOtter.State getState() {
  327.         return this.entityData.get(DATA_STATE_ID);
  328.     }
  329.  
  330.     public void switchToState(SeaOtter.State state) {
  331.         this.entityData.set(DATA_STATE_ID, state);
  332.         this.inStateTicks = 0L;
  333.     }
  334.  
  335.     public boolean isInAnimationTransition() {
  336.         return switch (this.getState()) {
  337.             case START_FLOATING -> this.inStateTicks < State.START_FLOATING.animationDuration();
  338.             case STOP_FLOATING -> this.inStateTicks < State.STOP_FLOATING.animationDuration();
  339.             case SIT_DOWN -> this.inStateTicks < State.SIT_DOWN.animationDuration();
  340.             case STAND_UP -> this.inStateTicks < State.STAND_UP.animationDuration();
  341.             default -> false;
  342.         };
  343.     }
  344.  
  345.     public boolean isFloating() {
  346.         return this.entityData.get(DATA_FLOATING_ID);
  347.     }
  348.  
  349.     public void setFloating(boolean value) {
  350.         this.entityData.set(DATA_FLOATING_ID, value);
  351.     }
  352.  
  353.     public boolean isSitting() {
  354.         return this.entityData.get(DATA_SITTING_ID);
  355.     }
  356.  
  357.     public void setSitting(boolean value) {
  358.         this.entityData.set(DATA_SITTING_ID, value);
  359.     }
  360.  
  361.     public void sitDown() {
  362.         if (!this.isSitting() && this.onGround() && !this.isInWater()) {
  363.             this.switchToState(State.SIT_DOWN);
  364.             this.setSitting(true);
  365.         }
  366.     }
  367.  
  368.     public void standUp() {
  369.         if (this.isSitting()) {
  370.             this.switchToState(State.STAND_UP);
  371.             this.setSitting(false);
  372.         }
  373.     }
  374.  
  375.     public boolean canSwim() {
  376.         return this.isInWater() || !this.needsToSurface() || !this.isFloating();
  377.     }
  378.  
  379.     public boolean wantsToSwim() {
  380.         return this.ticksSinceLastSwamAround > 200; /// 1 minute
  381.     }
  382.  
  383.     public void resetTicksSinceLastSwam() {
  384.         this.ticksSinceLastSwamAround = 0;
  385.     }
  386.  
  387.     public boolean needsToSurface() {
  388.         return this.needsToSurface;
  389.     }
  390.  
  391.     public void setNeedsToSurface(boolean value) {
  392.         this.needsToSurface = value;
  393.     }
  394.  
  395.     @Nullable
  396.     public BlockPos getSurfacePos() {
  397.         return this.surfacePos;
  398.     }
  399.  
  400.     public void setSurfacePos(@Nullable BlockPos pos) {
  401.         this.surfacePos = pos;
  402.     }
  403.  
  404.     public boolean wantsToGoInWater() {
  405.         return this.ticksOutOfWater > 200;
  406.     }
  407.  
  408.     public void resetTicksOutOfWater() {
  409.         this.ticksOutOfWater = 0;
  410.     }
  411.  
  412.     @Override
  413.     protected PathNavigation createNavigation(Level level) {
  414.         return new SeaOtter.SeaOtterPathNavigation(this, level);
  415.     }
  416.  
  417.     @Override
  418.     protected BodyRotationControl createBodyControl() {
  419.         return new SeaOtter.SeaOtterBodyRotationControl(this);
  420.     }
  421.  
  422.     @Override
  423.     public void travel(Vec3 pos) {
  424.         if (this.refuseToMove() && this.onGround()) {
  425.             this.setDeltaMovement(this.getDeltaMovement().multiply(0.0, 1.0, 0.0));
  426.             pos = pos.multiply(0.0, 1.0, 0.0);
  427.         }
  428.  
  429.         super.travel(pos);
  430.     }
  431.  
  432.     public boolean refuseToMove() {
  433.         return this.isSitting() || this.isInAnimationTransition();
  434.     }
  435.  
  436.     public boolean wantsToMove() {
  437.         return !this.getNavigation().isDone() || this.getTarget() != null || this.moveControl.hasWanted();
  438.     }
  439.  
  440.     public boolean isCloserThan(BlockPos pos, double distance) {
  441.         return pos.closerThan(this.blockPosition(), distance);
  442.     }
  443.    
  444.     public boolean hasReachedTarget(BlockPos pos, double distance) {
  445.         if (this.isCloserThan(pos, distance)) {
  446.             return true;
  447.         } else {
  448.             Path path = this.navigation.getPath();
  449.             return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
  450.         }
  451.     }
  452.  
  453.     public void pathfindDirectlyTowards(BlockPos pos, double speed) {
  454.         this.navigation.setMaxVisitedNodesMultiplier(10.0F);
  455.         this.navigation.moveTo(pos.getX(), pos.getY(), pos.getZ(), speed);
  456.         if (this.navigation.getPath() != null) {
  457.             this.navigation.getPath().canReach();
  458.         }
  459.     }
  460.  
  461.     @Override
  462.     public Vec3 getViewVector(float partialTicks) {
  463.         Vec3 view = super.getViewVector(partialTicks);
  464.         if (this.isFloating()) {
  465.             return view.reverse();
  466.         }
  467.         return view;
  468.     }
  469.  
  470.     @Override
  471.     public int getMaxHeadXRot() {
  472.         return super.getMaxHeadXRot();
  473.     }
  474.  
  475.     @Override
  476.     public int getMaxHeadYRot() {
  477.         if (this.isFloating()) {
  478.             return 2;
  479.         } else if (this.isSitting()) {
  480.             return 20;
  481.         } else {
  482.             return super.getMaxHeadYRot();
  483.         }
  484.     }
  485.  
  486.     @Override
  487.     public int getHeadRotSpeed() {
  488.         return this.isFloating() ? 5 : 10;
  489.     }
  490.  
  491.     @Override
  492.     public InteractionResult mobInteract(Player player, InteractionHand hand) {
  493.         return super.mobInteract(player, hand);
  494.     }
  495.  
  496.     @Override
  497.     public boolean isFood(ItemStack stack) {
  498.         return false;
  499.     }
  500.  
  501.     public static class SwimAroundGoal extends RandomSwimmingGoal {
  502.         private final SeaOtter seaOtter;
  503.         private int ticksSwimming;
  504.  
  505.         public SwimAroundGoal(SeaOtter mob, double speed, int interval) {
  506.             super(mob, speed, interval);
  507.             this.seaOtter = mob;
  508.         }
  509.  
  510.         @Override
  511.         public boolean canUse() {
  512.             if (!this.seaOtter.isInWater()) {
  513.                 return false;
  514.             } else if (this.seaOtter.isFloating()) {
  515.                 return false;
  516.             } else if (this.seaOtter.needsToSurface()) {
  517.                 return false;
  518.             }
  519.             return this.seaOtter.wantsToSwim() && super.canUse();
  520.         }
  521.  
  522.         @Override
  523.         public boolean canContinueToUse() {
  524.             return !this.seaOtter.needsToSurface() && (this.seaOtter.wantsToSwim() || this.ticksSwimming <= 200);
  525.         }
  526.  
  527.         @Override
  528.         public void start() {
  529.             super.start();
  530.             this.seaOtter.switchToState(State.SWIMMING);
  531.                     }
  532.  
  533.         @Override
  534.         public void stop() {
  535.             super.stop();
  536.             this.ticksSwimming = 0;
  537.             this.seaOtter.setNeedsToSurface(true);
  538.             this.seaOtter.resetTicksSinceLastSwam();
  539.         }
  540.  
  541.         @Override
  542.         protected Vec3 getPosition() {
  543.             return BehaviorUtils.getRandomSwimmablePos(this.mob, 10, 4);
  544.         }
  545.  
  546.         @Override
  547.         public void tick() {
  548.             if (this.seaOtter.needsToSurface()) {
  549.                 this.stop();
  550.                 return;
  551.             }
  552.  
  553.             this.trigger();
  554.             ++this.ticksSwimming;
  555.         }
  556.     }
  557.  
  558.     public static class MoveToWaterGoal extends MoveToBlockGoal {
  559.         private final SeaOtter seaOtter;
  560.  
  561.         public MoveToWaterGoal(SeaOtter mob, double speed) {
  562.             super(mob, speed, 24);
  563.             this.seaOtter = mob;
  564.             this.verticalSearchStart = -1;
  565.         }
  566.  
  567.         @Override
  568.         public boolean canUse() {
  569.             return !this.seaOtter.isInWater() && this.seaOtter.wantsToGoInWater() && super.canUse();
  570.         }
  571.  
  572.         @Override
  573.         public boolean canContinueToUse() {
  574.             return !this.seaOtter.isInWater() && this.tryTicks <= 1200 && this.isValidTarget(this.seaOtter.level(), this.blockPos);
  575.         }
  576.  
  577.         @Override
  578.         public boolean shouldRecalculatePath() {
  579.             return this.tryTicks % 160 == 0;
  580.         }
  581.  
  582.         @Override
  583.         protected boolean isValidTarget(LevelReader level, BlockPos pos) {
  584.             return level.getBlockState(pos).is(Blocks.WATER);
  585.         }
  586.     }
  587.  
  588.     public static class SwimToSurfaceGoal extends MoveToBlockGoal {
  589.         private final SeaOtter seaOtter;
  590.         private boolean reachedTarget;
  591.  
  592.         public SwimToSurfaceGoal(SeaOtter mob) {
  593.             super(mob, 1.0D, 24, 24);
  594.             this.seaOtter = mob;
  595.         }
  596.  
  597.         @Override
  598.         public boolean canUse() {
  599.             if (!this.seaOtter.isInWater() && this.seaOtter.wantsToSwim() && this.seaOtter.isFloating()) {
  600.                 return false;
  601.             }
  602.             return this.seaOtter.needsToSurface() && super.canUse();
  603.         }
  604.  
  605.         @Override
  606.         public boolean canContinueToUse() {
  607.             return !this.isReachedTarget();
  608.         }
  609.  
  610.         @Override
  611.         public void start() {
  612.             super.start();
  613.             this.seaOtter.switchToState(State.SWIMMING);
  614.             this.seaOtter.setSurfacePos(this.blockPos);
  615.         }
  616.  
  617.         @Override
  618.         public void stop() {
  619.             this.seaOtter.setNeedsToSurface(false);
  620.             this.seaOtter.getNavigation().stop();
  621.         }
  622.  
  623.         @Override
  624.         public void tick() {
  625.             BlockPos pos = this.getMoveToTarget();
  626.             Vec3 otterPos = this.seaOtter.position();
  627.             Vec3 targetCenter = Vec3.atCenterOf(this.blockPos.above());
  628.  
  629.             if (Math.abs(targetCenter.x - otterPos.x) < 0.5 && Math.abs(targetCenter.z - otterPos.z) < 0.5) {
  630.                 Vec3 motion = targetCenter.subtract(otterPos).normalize().scale(0.075D);
  631.                 this.seaOtter.setDeltaMovement(motion);
  632.                 this.seaOtter.getLookControl().setLookAt(pos.getX(), pos.getY(), pos.getZ());
  633.             }
  634.  
  635.             if (!pos.closerToCenterThan(this.seaOtter.position(), this.acceptedDistance())) {
  636.                 this.seaOtter.pathfindDirectlyTowards(pos,3.0D);
  637.                 this.seaOtter.getLookControl().setLookAt(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, (float)(this.seaOtter.getMaxHeadYRot() + 20), (float)this.seaOtter.getMaxHeadXRot());
  638.                 this.reachedTarget = false;
  639.             } else {
  640.                 this.reachedTarget = true;
  641.             }
  642.         }
  643.  
  644.         @Override
  645.         protected boolean isValidTarget(LevelReader level, BlockPos pos) {
  646.             return level.getBlockState(pos).is(Blocks.WATER) && level.getBlockState(pos.above()).isAir();
  647.         }
  648.  
  649.         @Override
  650.         protected boolean isReachedTarget() {
  651.             return this.reachedTarget;
  652.         }
  653.     }
  654.  
  655.     public static class FloatingGoal extends Goal {
  656.         private final SeaOtter seaOtter;
  657.         private Phase phase = Phase.NONE;
  658.  
  659.         public FloatingGoal(SeaOtter mob) {
  660.             this.seaOtter = mob;
  661.         }
  662.  
  663.         @Override
  664.         public boolean canUse() {
  665.             return this.seaOtter.isInWater() && !this.seaOtter.isFloating() && this.seaOtter.getSurfacePos() != null && this.seaOtter.hasReachedTarget(this.seaOtter.getSurfacePos(), 0.25D);
  666.         }
  667.  
  668.         @Override
  669.         public boolean canContinueToUse() {
  670.             return this.phase != Phase.NONE;
  671.         }
  672.  
  673.         @Override
  674.         public void start() {
  675.             this.seaOtter.setFloating(true);
  676.             this.seaOtter.switchToState(State.START_FLOATING);
  677.             this.phase = Phase.START_FLOATING;
  678.         }
  679.  
  680.         @Override
  681.         public void stop() {
  682.             this.seaOtter.setFloating(false);
  683.             this.seaOtter.setSurfacePos(null);
  684.         }
  685.  
  686.         @Override
  687.         public void tick() {
  688.             switch (this.phase) {
  689.                 case START_FLOATING -> {
  690.                     if (this.seaOtter.inStateTicks >= State.START_FLOATING.animationDuration()) {
  691.                         this.seaOtter.resetTicksSinceLastSwam();
  692.                         this.seaOtter.switchToState(State.FLOATING);
  693.                         this.seaOtter.setFloating(true);
  694.                         this.phase = Phase.FLOATING;
  695.                     }
  696.                 }
  697.                 case FLOATING -> {
  698.                     if (this.seaOtter.inStateTicks >= State.FLOATING.animationDuration() && this.seaOtter.wantsToSwim()) {
  699.                         this.seaOtter.switchToState(State.STOP_FLOATING);
  700.                         this.seaOtter.setFloating(true);
  701.                         this.phase = Phase.STOP_FLOATING;
  702.                     }
  703.                 }
  704.                 case STOP_FLOATING -> {
  705.                     if (this.seaOtter.inStateTicks >= State.STOP_FLOATING.animationDuration()) {
  706.                         this.seaOtter.switchToState(State.SWIMMING);
  707.                         this.seaOtter.setFloating(true);
  708.                         this.phase = Phase.NONE;
  709.                     }
  710.                 }
  711.             }
  712.         }
  713.  
  714.         enum Phase {
  715.             NONE,
  716.             START_FLOATING,
  717.             FLOATING,
  718.             STOP_FLOATING
  719.         }
  720.     }
  721.  
  722.     public static class SeaOtterMoveControl extends MoveControl {
  723.         private final SeaOtter seaOtter;
  724.  
  725.         public SeaOtterMoveControl(SeaOtter mob) {
  726.             super(mob);
  727.             this.seaOtter = mob;
  728.         }
  729.  
  730.         @Override
  731.         public void tick() {
  732.             if (this.seaOtter.isInWater()) {
  733.                 this.seaOtter.setDeltaMovement(this.seaOtter.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
  734.  
  735.                 if (this.operation == MoveControl.Operation.MOVE_TO && !this.seaOtter.getNavigation().isDone()) {
  736.                     double d0 = this.wantedX - this.seaOtter.getX();
  737.                     double d1 = this.wantedY - this.seaOtter.getY();
  738.                     double d2 = this.wantedZ - this.seaOtter.getZ();
  739.                     double d3 = d0 * d0 + d1 * d1 + d2 * d2;
  740.                     if (d3 < (double)2.5000003E-7F) {
  741.                         this.seaOtter.setZza(0.0F);
  742.                     } else {
  743.                         float f = (float)(Mth.atan2(d2, d0) * (double)(180F / (float)Math.PI)) - 90.0F;
  744.                         this.seaOtter.setYRot(this.rotlerp(this.seaOtter.getYRot(), f, (float)10));
  745.                         this.seaOtter.yBodyRot = this.seaOtter.getYRot();
  746.                         this.seaOtter.yHeadRot = this.seaOtter.getYRot();
  747.  
  748.                         float speed = (float)(this.speedModifier * this.seaOtter.getAttributeValue(Attributes.MOVEMENT_SPEED));
  749.                         this.seaOtter.setSpeed(speed * 0.02F);
  750.  
  751.                         double d4 = Math.sqrt(d0 * d0 + d2 * d2);
  752.                         if (Math.abs(d1) > (double)1.0E-5F || Math.abs(d4) > (double)1.0E-5F) {
  753.                             float f3 = -((float)(Mth.atan2(d1, d4) * (double)(180F / (float)Math.PI)));
  754.                             f3 = Mth.clamp(Mth.wrapDegrees(f3), (float)(-85), (float)85);
  755.                             this.seaOtter.setXRot(this.rotlerp(this.seaOtter.getXRot(), f3, 5.0F));
  756.                         }
  757.  
  758.                         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()) {
  759.                             this.seaOtter.getJumpControl().jump();
  760.                             this.seaOtter.setSpeed(speed);
  761.                         }
  762.  
  763.                         float f6 = Mth.cos(this.seaOtter.getXRot() * ((float)Math.PI / 180F));
  764.                         float f4 = Mth.sin(this.seaOtter.getXRot() * ((float)Math.PI / 180F));
  765.                         this.seaOtter.zza = f6 * speed;
  766.                         this.seaOtter.yya = -f4 * speed;
  767.                     }
  768.                 } else {
  769.                     this.seaOtter.setSpeed(0.0F);
  770.                     this.seaOtter.setXxa(0.0F);
  771.                     this.seaOtter.setYya(0.0F);
  772.                     this.seaOtter.setZza(0.0F);
  773.                 }
  774.             } else {
  775.                 if (this.hasWanted() && this.seaOtter.isSitting()) {
  776.                     this.seaOtter.standUp();
  777.                 }
  778.  
  779.                 if (!this.seaOtter.isFloating()) {
  780.                     super.tick();
  781.                 }
  782.             }
  783.         }
  784.     }
  785.  
  786.     public static class SeaOtterLookControl extends LookControl {
  787.         private final SeaOtter seaOtter;
  788.  
  789.         public SeaOtterLookControl(SeaOtter mob) {
  790.             super(mob);
  791.             this.seaOtter = mob;
  792.         }
  793.  
  794.         @Override
  795.         protected Optional<Float> getXRotD() {
  796.             if (this.seaOtter.isFloating() && super.getXRotD().isPresent()) {
  797.                 return Optional.of(-super.getXRotD().get());
  798.             }
  799.  
  800.             return super.getXRotD();
  801.         }
  802.  
  803.         @Override
  804.         protected Optional<Float> getYRotD() {
  805.             if (this.seaOtter.isFloating() && super.getYRotD().isPresent()) {
  806.                 return Optional.of(Mth.wrapDegrees(super.getYRotD().get() + 180.0F));
  807.             }
  808.  
  809.             return super.getYRotD();
  810.         }
  811.  
  812.         @Override
  813.         protected boolean resetXRotOnTick() {
  814.             boolean flag = this.seaOtter.getState() == State.START_FLOATING && this.seaOtter.inStateTicks < State.START_FLOATING.animationDuration();
  815.             boolean flag2 = this.seaOtter.getState() == State.STOP_FLOATING && this.seaOtter.inStateTicks < State.STOP_FLOATING.animationDuration();
  816.             return flag || flag2;
  817.         }
  818.  
  819.         @Override
  820.         public void tick() {
  821.             if (this.resetXRotOnTick()) {
  822.                 this.mob.setXRot(0.0F);
  823.             }
  824.  
  825.             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())) {
  826.                 if (this.seaOtter.isInWater()) {
  827.                     if (this.lookAtCooldown > 0) {
  828.                         --this.lookAtCooldown;
  829.                         this.getYRotD().ifPresent((i) -> this.seaOtter.yHeadRot = this.rotateTowards(this.seaOtter.yHeadRot, i + 20.0F, this.yMaxRotSpeed));
  830.                         this.getXRotD().ifPresent((i) -> this.seaOtter.setXRot(this.rotateTowards(this.seaOtter.getXRot(), i + 10.0F, this.xMaxRotAngle)));
  831.                     } else {
  832.                         if (this.seaOtter.getNavigation().isDone()) {
  833.                             this.seaOtter.setXRot(this.rotateTowards(this.seaOtter.getXRot(), 0.0F, 5.0F));
  834.                         }
  835.  
  836.                         this.seaOtter.yHeadRot = this.rotateTowards(this.seaOtter.yHeadRot, this.seaOtter.yBodyRot, this.yMaxRotSpeed);
  837.                     }
  838.                 } else {
  839.                     super.tick();
  840.                 }
  841.             }
  842.         }
  843.     }
  844.  
  845.     public class SeaOtterPathNavigation extends WaterBoundPathNavigation {
  846.  
  847.         public SeaOtterPathNavigation(Mob mob, Level level) {
  848.             super(mob, level);
  849.         }
  850.  
  851.         @Override
  852.         protected PathFinder createPathFinder(int nodes) {
  853.             this.nodeEvaluator = new AmphibiousNodeEvaluator(true);
  854.             return new PathFinder(this.nodeEvaluator, nodes);
  855.         }
  856.  
  857.         @Override
  858.         protected boolean canUpdatePath() {
  859.             return true;
  860.         }
  861.  
  862.         @Override
  863.         public boolean isStableDestination(BlockPos pos) {
  864.             BlockState belowState = this.level.getBlockState(pos.below());
  865.             if (SeaOtter.this.isInWater()) {
  866.                 return !(belowState.isAir() || belowState.getFluidState().is(FluidTags.WATER));
  867.             } else {
  868.                 return !belowState.isAir();
  869.             }
  870.         }
  871.     }
  872.  
  873.     public class SeaOtterBodyRotationControl extends BodyRotationControl {
  874.  
  875.         public SeaOtterBodyRotationControl(Mob mob) {
  876.             super(mob);
  877.         }
  878.  
  879.         @Override
  880.         public void clientTick() {
  881.             if (!SeaOtter.this.refuseToMove() && !SeaOtter.this.isFloating()) {
  882.                 super.clientTick();
  883.             }
  884.         }
  885.     }
  886.  
  887.     public enum State implements StringRepresentable {
  888.         NOTHING("nothing", 0, 0),
  889.         IDLE_IN_WATER("idle_in_water", 1, 50),
  890.         SWIMMING("swimming", 2, 40),
  891.         GLIDE_WHILE_SWIMMING("glide_while_swimming", 3, 40),
  892.         TWIRL_WHILE_SWIMMING("twirl_while_swimming", 4, 30),
  893.         FLOATING("floating", 5, 50),
  894.         START_FLOATING("start_floating", 6, 25),
  895.         STOP_FLOATING("stop_floating", 7, 40),
  896.         SWIMMING_WHILE_FLOATING("swimming_while_floating", 8, 50),
  897.         TWIRL_WHILE_FLOATING("twirl_while_floating", 9, 55),
  898.         SIT_DOWN("sit_down", 10, 30),
  899.         SITTING("sitting", 11, 60),
  900.         STAND_UP("stand_up", 12, 40),
  901.         WALKING("walking", 13, 45);
  902.  
  903.         private static final IntFunction<SeaOtter.State> BY_ID = ByIdMap.continuous(SeaOtter.State::id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
  904.         public static final StreamCodec<ByteBuf, SeaOtter.State> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, SeaOtter.State::id);
  905.         private final String name;
  906.         private final int id;
  907.         private final int animationDuration;
  908.  
  909.         State(final String name, final int id, final int animationDuration) {
  910.             this.name = name;
  911.             this.id = id;
  912.             this.animationDuration = animationDuration;
  913.         }
  914.  
  915.         public static SeaOtter.State fromName(String name) {
  916.             return StringRepresentable.fromEnum(SeaOtter.State::values).byName(name, NOTHING);
  917.         }
  918.  
  919.         @Override
  920.         public String getSerializedName() {
  921.             return this.name;
  922.         }
  923.  
  924.         private int id() {
  925.             return this.id;
  926.         }
  927.  
  928.         public int animationDuration() {
  929.             return this.animationDuration;
  930.         }
  931.     }
  932.  
  933.     private enum Action {
  934.         IDLING,
  935.         SWIMMING_TO_SURFACE,
  936.         SWIMMING,
  937.         WALKING_TO_WATER
  938.     }
  939.  
  940. }
Advertisement
Add Comment
Please, Sign In to add comment