jayhillx

butterfly (beta)

Sep 10th, 2023
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 30.62 KB | None | 0 0
  1. package com.mysticsbiomes.common.entity.animal.butterfly;
  2.  
  3. import com.google.common.collect.Maps;
  4. import com.mysticsbiomes.common.block.entity.ButterflyNestBlockEntity;
  5. import com.mysticsbiomes.init.MysticBlocks;
  6. import com.mysticsbiomes.init.MysticPoiTypes;
  7. import com.mysticsbiomes.init.MysticSounds;
  8. import net.minecraft.core.BlockPos;
  9. import net.minecraft.core.particles.ParticleOptions;
  10. import net.minecraft.core.particles.ParticleTypes;
  11. import net.minecraft.nbt.CompoundTag;
  12. import net.minecraft.nbt.NbtUtils;
  13. import net.minecraft.network.syncher.EntityDataAccessor;
  14. import net.minecraft.network.syncher.EntityDataSerializers;
  15. import net.minecraft.network.syncher.SynchedEntityData;
  16. import net.minecraft.server.level.ServerLevel;
  17. import net.minecraft.sounds.SoundEvent;
  18. import net.minecraft.tags.BlockTags;
  19. import net.minecraft.tags.ItemTags;
  20. import net.minecraft.util.ByIdMap;
  21. import net.minecraft.util.Mth;
  22. import net.minecraft.util.StringRepresentable;
  23. import net.minecraft.world.DifficultyInstance;
  24. import net.minecraft.world.InteractionHand;
  25. import net.minecraft.world.InteractionResult;
  26. import net.minecraft.world.damagesource.DamageSource;
  27. import net.minecraft.world.entity.*;
  28. import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
  29. import net.minecraft.world.entity.ai.attributes.Attributes;
  30. import net.minecraft.world.entity.ai.control.FlyingMoveControl;
  31. import net.minecraft.world.entity.ai.control.LookControl;
  32. import net.minecraft.world.entity.ai.goal.FloatGoal;
  33. import net.minecraft.world.entity.ai.goal.Goal;
  34. import net.minecraft.world.entity.ai.goal.TemptGoal;
  35. import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
  36. import net.minecraft.world.entity.ai.navigation.PathNavigation;
  37. import net.minecraft.world.entity.ai.util.AirAndWaterRandomPos;
  38. import net.minecraft.world.entity.ai.util.AirRandomPos;
  39. import net.minecraft.world.entity.ai.util.HoverRandomPos;
  40. import net.minecraft.world.entity.ai.village.poi.PoiManager;
  41. import net.minecraft.world.entity.ai.village.poi.PoiRecord;
  42. import net.minecraft.world.entity.animal.Animal;
  43. import net.minecraft.world.entity.animal.FlyingAnimal;
  44. import net.minecraft.world.entity.player.Player;
  45. import net.minecraft.world.item.crafting.Ingredient;
  46. import net.minecraft.world.level.Level;
  47. import net.minecraft.world.level.LevelReader;
  48. import net.minecraft.world.level.ServerLevelAccessor;
  49. import net.minecraft.world.level.block.Block;
  50. import net.minecraft.world.level.block.Blocks;
  51. import net.minecraft.world.level.block.DoublePlantBlock;
  52. import net.minecraft.world.level.block.entity.BlockEntity;
  53. import net.minecraft.world.level.block.state.BlockState;
  54. import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
  55. import net.minecraft.world.level.gameevent.GameEvent;
  56. import net.minecraft.world.level.pathfinder.Path;
  57. import net.minecraft.world.phys.Vec3;
  58. import org.jetbrains.annotations.Nullable;
  59.  
  60. import java.util.*;
  61. import java.util.function.Predicate;
  62. import java.util.stream.Collectors;
  63. import java.util.stream.Stream;
  64.  
  65. /**
  66.  * Butterflies are friendly ambient anthropoids, useful for growing flowers.
  67.  */
  68. public class Butterfly extends Animal implements FlyingAnimal {
  69.     private static final EntityDataAccessor<Integer> DATA_TYPE_ID = SynchedEntityData.defineId(Butterfly.class, EntityDataSerializers.INT);
  70.     private boolean sleeping;
  71.     private int ticksSinceLastSlept;
  72.     private int remainingTicksBeforeLocatingNewNest;
  73.     private int remainingCooldownBeforeCanPollinate; // 2400 ticks (2 minutes)
  74.     private boolean hasNectar;
  75.     private int nectarPoints;
  76.     @Nullable
  77.     private BlockPos nestPos;
  78.     @Nullable
  79.     private Block givenFlower;
  80.     Butterfly.PollinateFlowerGoal pollinateGoal;
  81.     private int underWaterTicks;
  82.  
  83.     public final AnimationState flyingAnimationState = new AnimationState();
  84.  
  85.     public Butterfly(EntityType<? extends Butterfly> entityType, Level level) {
  86.         super(entityType, level);
  87.         this.moveControl = new FlyingMoveControl(this, 20, true);
  88.         this.lookControl = new ButterflyLookControl(this);
  89.     }
  90.  
  91.     protected void defineSynchedData() {
  92.         super.defineSynchedData();
  93.         this.entityData.define(DATA_TYPE_ID, 0);
  94.     }
  95.  
  96.     protected void registerGoals() {
  97.         this.goalSelector.addGoal(0,  new Butterfly.PlantFlowerGoal());
  98.         this.pollinateGoal = new Butterfly.PollinateFlowerGoal();
  99.         this.goalSelector.addGoal(1, this.pollinateGoal);
  100.         this.goalSelector.addGoal(2, new Butterfly.EnterNestGoal());
  101.         this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(ItemTags.FLOWERS), false));
  102.         this.goalSelector.addGoal(4, new Butterfly.LocateNestGoal());
  103.         this.goalSelector.addGoal(4, new Butterfly.GoToNestGoal());
  104.         this.goalSelector.addGoal(5, new Butterfly.WanderGoal());
  105.         this.goalSelector.addGoal(6, new FloatGoal(this));
  106.     }
  107.  
  108.     public static AttributeSupplier.Builder createAttributes() {
  109.         return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FLYING_SPEED, 0.6F).add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.FOLLOW_RANGE, 28.0D);
  110.     }
  111.  
  112.     @Override
  113.     public void addAdditionalSaveData(CompoundTag tag) {
  114.         super.addAdditionalSaveData(tag);
  115.         tag.putString("Type", this.getVariant().getSerializedName());
  116.         tag.putBoolean("IsSleeping", this.isSleeping());
  117.         tag.putBoolean("HasNectar", this.hasNectar());
  118.         tag.putInt("NectarPoints", this.nectarPoints);
  119.         tag.putInt("TicksSinceLastSlept", this.ticksSinceLastSlept);
  120.         tag.putInt("TicksBeforeCanPollinate", this.remainingCooldownBeforeCanPollinate);
  121.  
  122.         if (this.nestPos != null) {
  123.             tag.put("NestPos", NbtUtils.writeBlockPos(this.nestPos));
  124.         }
  125.     }
  126.  
  127.     @Override
  128.     public void readAdditionalSaveData(CompoundTag tag) {
  129.         super.readAdditionalSaveData(tag);
  130.         this.setVariant(Type.byName(tag.getString("Type")));
  131.         this.sleeping = tag.getBoolean("IsSleeping");
  132.         this.hasNectar = tag.contains("HasNectar");
  133.         this.nectarPoints = tag.getInt("NectarPoints");
  134.         this.ticksSinceLastSlept = tag.getInt("TicksSinceLastSlept");
  135.         this.remainingCooldownBeforeCanPollinate = tag.getInt("TicksBeforeCanPollinate");
  136.  
  137.         this.nestPos = null;
  138.         if (tag.contains("NestPos")) {
  139.             this.nestPos = NbtUtils.readBlockPos(tag.getCompound("NestPos"));
  140.         }
  141.     }
  142.  
  143.     public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
  144.         return null;
  145.     }
  146.  
  147.     public MobType getMobType() {
  148.         return MobType.ARTHROPOD;
  149.     }
  150.  
  151.     public Type getVariant() {
  152.         return Type.byId(this.entityData.get(DATA_TYPE_ID));
  153.     }
  154.  
  155.     public void setVariant(Type type) {
  156.         this.entityData.set(DATA_TYPE_ID, type.getId());
  157.     }
  158.  
  159.     @Override
  160.     public void tick() {
  161.         super.tick();
  162.  
  163.         //if (this.level().isClientSide()) {
  164.             //this.flyingAnimationState.animateWhen(this.isFlying(), this.tickCount);
  165.         //}
  166.  
  167.         if (this.hasNectar() && this.random.nextFloat() < 0.05F) {
  168.             for (int i = 0; i < this.random.nextInt(2) + 1; ++i) {
  169.                 this.level().addParticle(ParticleTypes.FALLING_NECTAR, Mth.lerp(this.level().random.nextDouble(), this.getX() - (double)0.3F, this.getX() + (double)0.3F), this.getY(0.5D), Mth.lerp(this.level().random.nextDouble(), this.getZ() - (double)0.3F, this.getZ() + (double)0.3F), 0.0D, 0.0D, 0.0D);
  170.             }
  171.         }
  172.     }
  173.  
  174.     @Override
  175.     public void aiStep() {
  176.         super.aiStep();
  177.  
  178.         if (!this.level().isClientSide) {
  179.             if (this.remainingTicksBeforeLocatingNewNest > 0) {
  180.                 --this.remainingTicksBeforeLocatingNewNest;
  181.             }
  182.  
  183.             if (this.remainingCooldownBeforeCanPollinate > 0) {
  184.                 --this.remainingCooldownBeforeCanPollinate;
  185.             }
  186.  
  187.             if (this.tickCount % 20 == 0 && !this.isNestValid()) {
  188.                 this.nestPos = null;
  189.             }
  190.         }
  191.     }
  192.  
  193.     @Override
  194.     protected void customServerAiStep() {
  195.         super.customServerAiStep();
  196.  
  197.         if (this.isInWaterOrBubble()) {
  198.             ++this.underWaterTicks;
  199.         } else {
  200.             this.underWaterTicks = 0;
  201.         }
  202.  
  203.         if (this.underWaterTicks > 20) {
  204.             this.hurt(this.damageSources().drown(), 1.0F);
  205.         }
  206.  
  207.         if (!this.isSleeping()) {
  208.             ++this.ticksSinceLastSlept;
  209.         }
  210.     }
  211.  
  212.     protected void addParticlesAroundSelf(ParticleOptions options) {
  213.         for (int i = 0; i < 5; ++i) {
  214.             double d0 = this.random.nextGaussian() * 0.02D;
  215.             double d1 = this.random.nextGaussian() * 0.02D;
  216.             double d2 = this.random.nextGaussian() * 0.02D;
  217.             this.level().addParticle(options, this.getRandomX(1.0D), this.getRandomY() + 1.0D, this.getRandomZ(1.0D), d0, d1, d2);
  218.         }
  219.     }
  220.  
  221.     protected void playStepSound(BlockPos pos, BlockState state) {
  222.     }
  223.  
  224.     protected float getSoundVolume() {
  225.         return 0.4F;
  226.     }
  227.  
  228.     protected SoundEvent getAmbientSound() {
  229.         return MysticSounds.BUTTERFLY_AMBIENT.get();
  230.     }
  231.  
  232.     protected SoundEvent getHurtSound(DamageSource source) {
  233.         return MysticSounds.BUTTERFLY_HURT.get();
  234.     }
  235.  
  236.     protected SoundEvent getDeathSound() {
  237.         return MysticSounds.BUTTERFLY_DEATH.get();
  238.     }
  239.  
  240.     protected PathNavigation createNavigation(Level level) {
  241.         FlyingPathNavigation navigation = new FlyingPathNavigation(this, level) {
  242.             public boolean isStableDestination(BlockPos p_27947_) {
  243.                 return !this.level.getBlockState(p_27947_.below()).isAir();
  244.             }
  245.         };
  246.         navigation.setCanOpenDoors(false);
  247.         navigation.setCanFloat(false);
  248.         navigation.setCanPassDoors(true);
  249.         return navigation;
  250.     }
  251.  
  252.     protected void checkFallDamage(double distance, boolean b, BlockState state, BlockPos pos) {
  253.     }
  254.  
  255.     protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
  256.         return dimensions.height * 0.5F;
  257.     }
  258.  
  259.     public float getWalkTargetValue(BlockPos pos, LevelReader reader) {
  260.         return reader.getBlockState(pos).isAir() ? 5.0F : 0.0F;
  261.     }
  262.  
  263.     public boolean isFlying() {
  264.         return !this.onGround();
  265.     }
  266.  
  267.     public boolean isTired() {
  268.         return this.ticksSinceLastSlept > 18000;
  269.     }
  270.  
  271.     public boolean isSleeping() {
  272.         return this.sleeping;
  273.     }
  274.  
  275.     public void setIsSleeping(boolean sleeping) {
  276.         this.sleeping = sleeping;
  277.     }
  278.  
  279.     private boolean isNestValid() {
  280.         if (this.nestPos == null) {
  281.             return false;
  282.         } else if (this.isTooFarAway(this.nestPos)) {
  283.             return false;
  284.         } else {
  285.             BlockEntity blockEntity = this.level().getBlockEntity(this.nestPos);
  286.             return blockEntity instanceof ButterflyNestBlockEntity;
  287.         }
  288.     }
  289.  
  290.     private boolean isNestNearFire() {
  291.         if (this.nestPos == null) {
  292.             return false;
  293.         } else {
  294.             BlockEntity blockEntity = this.level().getBlockEntity(this.nestPos);
  295.             return blockEntity instanceof ButterflyNestBlockEntity && ((ButterflyNestBlockEntity)blockEntity).isFireNearby();
  296.         }
  297.     }
  298.  
  299.     private boolean doesNestHaveSpace(BlockPos pos) {
  300.         BlockEntity blockEntity = this.level().getBlockEntity(pos);
  301.  
  302.         if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  303.             return !entity.isFull();
  304.         } else {
  305.             return false;
  306.         }
  307.     }
  308.  
  309.     private boolean wantsToEnterNest() {
  310.         boolean flag = this.isTired() || this.level().isRaining() || this.level().isNight() || this.hasNectar();
  311.         return flag && !this.isNestNearFire();
  312.     }
  313.  
  314.     private boolean wasGivenFlower() {
  315.         return this.givenFlower != null;
  316.     }
  317.  
  318.     public boolean hasNectar() {
  319.         return this.hasNectar;
  320.     }
  321.  
  322.     public void setHasNectar(boolean hasNectar) {
  323.         this.hasNectar = hasNectar;
  324.     }
  325.  
  326.     public boolean canPollinate() {
  327.         return this.remainingCooldownBeforeCanPollinate == 0;
  328.     }
  329.  
  330.     /** determines when a butterfly wants to pollinate after their cooldown is over. */
  331.     public boolean wantsToPollinate() {
  332.         return this.canPollinate() && this.random.nextInt(120) == 0;
  333.     }
  334.  
  335.     /**
  336.      * @return if the butterfly has a home, is not tired, does not have nectar, and cooldown is at 0.
  337.      */
  338.     public boolean canSpreadFlowers() {
  339.         boolean flag = !this.hasNectar || this.nestPos != null || !this.isTired();
  340.         return flag && this.canPollinate();
  341.     }
  342.  
  343.     @Override
  344.     public InteractionResult mobInteract(Player player, InteractionHand hand) {
  345.         if (player.getItemInHand(hand).is(ItemTags.SMALL_FLOWERS)) {
  346.  
  347.             if (this.canPollinate() && !Butterfly.this.hasNectar()) {
  348.                 this.givenFlower = Block.byItem(player.getItemInHand(hand).getItem());
  349.  
  350.                 this.addParticlesAroundSelf(ParticleTypes.HAPPY_VILLAGER);
  351.                 return InteractionResult.SUCCESS;
  352.             } else {
  353.                 this.addParticlesAroundSelf(ParticleTypes.ANGRY_VILLAGER);
  354.                 return InteractionResult.PASS;
  355.             }
  356.         } else {
  357.             return super.mobInteract(player, hand);
  358.         }
  359.     }
  360.  
  361.     @Override
  362.     public SpawnGroupData finalizeSpawn(ServerLevelAccessor accessor, DifficultyInstance instance, MobSpawnType type, @Nullable SpawnGroupData data, @Nullable CompoundTag tag) {
  363.         data = super.finalizeSpawn(accessor, instance, type, data, tag);
  364.         this.setVariant(Type.byId(random.nextInt(3)));
  365.         return data;
  366.     }
  367.  
  368.     private Optional<BlockPos> findNearestBlock(Predicate<BlockState> predicate, double distance) {
  369.         BlockPos pos = Butterfly.this.blockPosition();
  370.         BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
  371.  
  372.         for (int i = 0; (double)i <= distance; i = i > 0 ? -i : 1 - i) {
  373.             for (int j = 0; (double)j < distance; ++j) {
  374.                 for (int k = 0; k <= j; k = k > 0 ? -k : 1 - k) {
  375.                     for (int l = k < j && k > -j ? j : 0; l <= j; l = l > 0 ? -l : 1 - l) {
  376.                         mutablePos.setWithOffset(pos, k, i - 1, l);
  377.  
  378.                         if (pos.closerThan(mutablePos, distance) && predicate.test(Butterfly.this.level().getBlockState(mutablePos))) {
  379.                             return Optional.of(mutablePos);
  380.                         }
  381.                     }
  382.                 }
  383.             }
  384.         }
  385.         return Optional.empty();
  386.     }
  387.  
  388.     private void pathfindRandomlyTowards(BlockPos pos) {
  389.         Vec3 vec3 = Vec3.atBottomCenterOf(pos);
  390.         int i = 0;
  391.         BlockPos pos1 = this.blockPosition();
  392.         int j = (int)vec3.y - pos1.getY();
  393.         if (j > 2) {
  394.             i = 4;
  395.         } else if (j < -2) {
  396.             i = -4;
  397.         }
  398.  
  399.         int k = 6;
  400.         int l = 8;
  401.         int i1 = pos1.distManhattan(pos);
  402.         if (i1 < 15) {
  403.             k = i1 / 2;
  404.             l = i1 / 2;
  405.         }
  406.  
  407.         Vec3 vec31 = AirRandomPos.getPosTowards(this, k, l, i, vec3, (float)Math.PI / 10F);
  408.         if (vec31 != null) {
  409.             this.navigation.setMaxVisitedNodesMultiplier(0.5F);
  410.             this.navigation.moveTo(vec31.x, vec31.y, vec31.z, 1.0D);
  411.         }
  412.     }
  413.  
  414.     private boolean pathfindDirectlyTowards(BlockPos pos) {
  415.         Butterfly.this.navigation.setMaxVisitedNodesMultiplier(10.0F);
  416.         Butterfly.this.navigation.moveTo(pos.getX(), pos.getY(), pos.getZ(), 1.0D);
  417.         return Butterfly.this.navigation.getPath() != null && Butterfly.this.navigation.getPath().canReach();
  418.     }
  419.  
  420.     private boolean isCloserThan(BlockPos pos, int distance) {
  421.         return pos.closerThan(this.blockPosition(), distance);
  422.     }
  423.  
  424.     private boolean isTooFarAway(BlockPos pos) {
  425.         return !this.isCloserThan(pos, 32);
  426.     }
  427.  
  428.     private boolean hasReachedPosition(BlockPos pos) {
  429.         if (this.isCloserThan(pos, 0)) {
  430.             return true;
  431.         } else {
  432.             Path path = this.navigation.getPath();
  433.             return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
  434.         }
  435.     }
  436.  
  437.     /**
  438.      * Sets the butterflies home/nest position by scoping out an available one nearby.
  439.      */
  440.     class LocateNestGoal extends Goal {
  441.  
  442.         public boolean canUse() {
  443.             return Butterfly.this.remainingTicksBeforeLocatingNewNest == 0 && Butterfly.this.nestPos == null && Butterfly.this.wantsToEnterNest();
  444.         }
  445.  
  446.         public boolean canContinueToUse() {
  447.             return false;
  448.         }
  449.  
  450.         public void start() {
  451.             Butterfly.this.remainingTicksBeforeLocatingNewNest = 200;
  452.  
  453.             List<BlockPos> list = this.findNearbyNestsWithSpace();
  454.             if (!list.isEmpty()) {
  455.                 for (BlockPos pos : list) {
  456.                     Butterfly.this.nestPos = pos;
  457.                     return;
  458.                 }
  459.                 Butterfly.this.nestPos = list.get(0);
  460.             }
  461.         }
  462.  
  463.         private List<BlockPos> findNearbyNestsWithSpace() {
  464.             BlockPos pos = Butterfly.this.blockPosition();
  465.             PoiManager poiManager = ((ServerLevel)Butterfly.this.level()).getPoiManager();
  466.  
  467.             Stream<PoiRecord> stream = poiManager.getInRange((holder) -> holder.is(MysticPoiTypes.BUTTERFLY_NEST.getId()), pos, 20, PoiManager.Occupancy.ANY);
  468.             return stream.map(PoiRecord::getPos).filter(Butterfly.this::doesNestHaveSpace).sorted(Comparator.comparingDouble((pos1) -> pos1.distSqr(pos))).collect(Collectors.toList());
  469.         }
  470.     }
  471.  
  472.     class GoToNestGoal extends Goal {
  473.         @Nullable
  474.         private Path lastPath;
  475.  
  476.         GoToNestGoal() {
  477.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  478.         }
  479.  
  480.         public boolean canUse() {
  481.             return Butterfly.this.nestPos != null && Butterfly.this.wantsToEnterNest() && !Butterfly.this.hasReachedPosition(Butterfly.this.nestPos) && Butterfly.this.level().getBlockState(Butterfly.this.nestPos).is(MysticBlocks.BUTTERFLY_NEST.get());
  482.         }
  483.  
  484.         public boolean canContinueToUse() {
  485.             return this.canUse();
  486.         }
  487.  
  488.         public void stop() {
  489.             Butterfly.this.navigation.stop();
  490.             Butterfly.this.navigation.resetMaxVisitedNodesMultiplier();
  491.         }
  492.  
  493.         public void tick() {
  494.             if (Butterfly.this.nestPos != null) {
  495.  
  496.                 if (!Butterfly.this.navigation.isInProgress()) {
  497.                     if (!Butterfly.this.isCloserThan(Butterfly.this.nestPos, 16)) {
  498.                         Butterfly.this.pathfindRandomlyTowards(Butterfly.this.nestPos);
  499.                     } else {
  500.                         boolean flag = Butterfly.this.pathfindDirectlyTowards(Butterfly.this.nestPos);
  501.  
  502.                         if (flag) {
  503.                             if (this.lastPath != null && Butterfly.this.navigation.getPath() != null && !Butterfly.this.navigation.getPath().sameAs(this.lastPath)) {
  504.                                 this.lastPath = Butterfly.this.navigation.getPath();
  505.                             }
  506.                         }
  507.                     }
  508.                 }
  509.             }
  510.         }
  511.     }
  512.  
  513.     class EnterNestGoal extends Goal {
  514.  
  515.         public boolean canUse() {
  516.             if (Butterfly.this.nestPos != null && Butterfly.this.wantsToEnterNest() && Butterfly.this.nestPos.closerToCenterThan(Butterfly.this.position(), 2.0D)) {
  517.                 BlockEntity blockEntity = Butterfly.this.level().getBlockEntity(Butterfly.this.nestPos);
  518.  
  519.                 if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  520.                     if (!entity.isFull()) {
  521.                         return true;
  522.                     }
  523.  
  524.                     Butterfly.this.nestPos = null;
  525.                 }
  526.             }
  527.             return false;
  528.         }
  529.  
  530.         public boolean canContinueToUse() {
  531.             return false;
  532.         }
  533.  
  534.         public void start() {
  535.             Butterfly.this.sleeping = true;
  536.  
  537.             if (Butterfly.this.nestPos != null) {
  538.                 BlockEntity blockEntity = Butterfly.this.level().getBlockEntity(Butterfly.this.nestPos);
  539.  
  540.                 if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  541.                     entity.addOccupant(Butterfly.this, Butterfly.this.hasNectar());
  542.                 }
  543.             }
  544.         }
  545.  
  546.         public void stop() {
  547.             Butterfly.this.ticksSinceLastSlept = 0;
  548.             Butterfly.this.sleeping = false;
  549.         }
  550.     }
  551.  
  552.     /**
  553.      * Either pollinates a random flower or locates and pollinates the same flower a player gave them.
  554.      */
  555.     class PollinateFlowerGoal extends Goal {
  556.         private final Predicate<BlockState> VALID_POLLINATION_BLOCKS = (state) -> {
  557.             if (state.is(BlockTags.FLOWERS)) {
  558.                 if (state.is(Blocks.SUNFLOWER)) {
  559.                     return state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER;
  560.                 } else {
  561.                     return true;
  562.                 }
  563.             } else {
  564.                 return false;
  565.             }
  566.         };
  567.         private int pollinatingTicks;
  568.         private boolean pollinating;
  569.         @Nullable
  570.         private BlockPos flowerPos;
  571.  
  572.         PollinateFlowerGoal() {
  573.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  574.         }
  575.  
  576.         public boolean canUse() {
  577.             if (Butterfly.this.level().isRaining()) {
  578.                 return false;
  579.             } else {
  580.                 if (Butterfly.this.canPollinate()) {
  581.                     if (this.flowerPos != null) {
  582.                         return false;
  583.                     }
  584.                     return Butterfly.this.wasGivenFlower() ? Butterfly.this.canSpreadFlowers() : Butterfly.this.wantsToPollinate();
  585.                 } else {
  586.                     return false;
  587.                 }
  588.             }
  589.         }
  590.  
  591.         public boolean canContinueToUse() {
  592.             if (Butterfly.this.level().isRaining()) {
  593.                 return false;
  594.             } else {
  595.                 return this.pollinatingTicks < 600; // 30 seconds to pollinate
  596.             }
  597.         }
  598.  
  599.         public void start() {
  600.             this.pollinatingTicks = 0;
  601.             this.pollinating = true;
  602.  
  603.             Optional<BlockPos> optional = Butterfly.this.givenFlower != null ? Butterfly.this.findNearestBlock((block) -> block.is(Butterfly.this.givenFlower), 16.0D) : Butterfly.this.findNearestBlock(VALID_POLLINATION_BLOCKS, 4.0D);
  604.             if (optional.isPresent() && this.flowerPos == null) {
  605.                 this.flowerPos = optional.get();
  606.             }
  607.         }
  608.  
  609.         public void stop() {
  610.             this.pollinating = false;
  611.  
  612.             Butterfly.this.setHasNectar(true);
  613.             Butterfly.this.nectarPoints += 2;
  614.  
  615.             if (!Butterfly.this.wasGivenFlower()) {
  616.                 this.flowerPos = null;
  617.                 Butterfly.this.navigation.stop();
  618.                 Butterfly.this.remainingCooldownBeforeCanPollinate = 3600; // 3 minute cooldown
  619.             }
  620.         }
  621.  
  622.         public boolean requiresUpdateEveryTick() {
  623.             return true;
  624.         }
  625.  
  626.         public void tick() {
  627.             if (this.flowerPos != null) {
  628.                 if (Butterfly.this.hasReachedPosition(this.flowerPos)) {
  629.                     Vec3 hoverPos = Vec3.atBottomCenterOf(this.flowerPos).add(0.0D, 0.6F, 0.0D);
  630.                     Butterfly.this.getMoveControl().setWantedPosition(hoverPos.x(), hoverPos.y(), hoverPos.z(), 0.35F);
  631.                     Butterfly.this.navigation.stop();
  632.  
  633.                     ++this.pollinatingTicks;
  634.                 } else {
  635.                     Butterfly.this.navigation.moveTo(this.flowerPos.getX() + 0.5D, this.flowerPos.getY() + 0.5D, this.flowerPos.getZ() + 0.5D, 0.85F);
  636.                 }
  637.             }
  638.         }
  639.  
  640.         boolean isPollinating() {
  641.             return this.pollinating;
  642.         }
  643.     }
  644.  
  645.     /**
  646.      * The main feature of butterflies; they plant 2 flowers for every 1 flower they collect nectar from.
  647.      */
  648.     class PlantFlowerGoal extends Goal {
  649.         private int plantingTicks;
  650.         @Nullable
  651.         private BlockPos emptyPos;
  652.  
  653.         PlantFlowerGoal() {
  654.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  655.         }
  656.  
  657.         public boolean canUse() {
  658.             return Butterfly.this.wasGivenFlower() && Butterfly.this.hasNectar() && Butterfly.this.nectarPoints > 0;
  659.         }
  660.  
  661.         public boolean canContinueToUse() {
  662.             return this.plantingTicks < 1200; // 1 minute
  663.         }
  664.  
  665.         public void start() {
  666.             this.plantingTicks = 0;
  667.             this.emptyPos = this.findRandomEmptyPos();
  668.         }
  669.  
  670.         public void stop() {
  671.             if (!Butterfly.this.level().isClientSide) {
  672.                 if (emptyPos != null) {
  673.                     int x = Mth.floor(this.emptyPos.getX());
  674.                     int y = Mth.floor(this.emptyPos.getY());
  675.                     int z = Mth.floor(this.emptyPos.getZ());
  676.                     BlockPos butterflyPos = new BlockPos(x, y, z);
  677.  
  678.                     if (Butterfly.this.givenFlower != null) {
  679.                         BlockState flowerState = Butterfly.this.givenFlower.defaultBlockState();
  680.  
  681.                         if (Butterfly.this.level().isEmptyBlock(butterflyPos) && flowerState.canSurvive(Butterfly.this.level(), butterflyPos)) {
  682.                             Butterfly.this.level().setBlockAndUpdate(butterflyPos, flowerState);
  683.                             Butterfly.this.level().gameEvent(GameEvent.BLOCK_PLACE, butterflyPos, GameEvent.Context.of(Butterfly.this, flowerState));
  684.                         }
  685.  
  686.                         Butterfly.this.nectarPoints -= 1;
  687.                         this.emptyPos = null;
  688.                     }
  689.                 }
  690.             }
  691.  
  692.             if (Butterfly.this.nectarPoints == 0) {
  693.                 Butterfly.this.givenFlower = null;
  694.                 Butterfly.this.pollinateGoal.flowerPos = null;
  695.                 Butterfly.this.setHasNectar(false);
  696.  
  697.                 Butterfly.this.navigation.stop();
  698.                 Butterfly.this.remainingCooldownBeforeCanPollinate = 3600; // 3 minute cooldown
  699.             }
  700.         }
  701.  
  702.         public boolean requiresUpdateEveryTick() {
  703.             return true;
  704.         }
  705.  
  706.         public void tick() {
  707.             if (this.emptyPos != null) {
  708.                 if (Butterfly.this.hasReachedPosition(this.emptyPos)) {
  709.                     Vec3 hoverPos = Vec3.atBottomCenterOf(this.emptyPos).add(0.0D, 0.6F, 0.0D);
  710.                     Butterfly.this.getMoveControl().setWantedPosition(hoverPos.x(), hoverPos.y(), hoverPos.z(), 0.35F);
  711.                     Butterfly.this.navigation.stop();
  712.  
  713.                     ++this.plantingTicks;
  714.                 } else {
  715.                     Butterfly.this.navigation.moveTo(this.emptyPos.getX() + 0.5D, this.emptyPos.getY() + 0.5D, this.emptyPos.getZ() + 0.5D, 0.85F);
  716.                 }
  717.             }
  718.         }
  719.  
  720.         /**
  721.          * @return a random empty space near the original flower pos a butterfly can plant a flower at.
  722.          */
  723.         private BlockPos findRandomEmptyPos() {
  724.             Map<Integer, BlockPos> map = Maps.newHashMap();
  725.  
  726.             BlockPos flowerPos = Butterfly.this.pollinateGoal.flowerPos;
  727.             BlockPos currentPos = flowerPos != null ? flowerPos : Butterfly.this.blockPosition();
  728.  
  729.             for (int i = 0; i <= 14; i++) {
  730.                 BlockPos pos;
  731.                 int x = Mth.floor(currentPos.getX() + (random.nextBoolean() ? random.nextInt(3) : -random.nextInt(3)));
  732.                 int y = Mth.floor(currentPos.getY());
  733.                 int z = Mth.floor(currentPos.getZ() + (random.nextBoolean() ? random.nextInt(3) : -random.nextInt(3)));
  734.                 pos = new BlockPos(x, y, z);
  735.  
  736.                 if (Butterfly.this.level().getBlockState(pos).isAir() && Butterfly.this.level().getBlockState(pos.below()).is(Blocks.GRASS_BLOCK)) {
  737.                     map.put(i, pos);
  738.                 }
  739.             }
  740.             return map.get(0);
  741.         }
  742.     }
  743.  
  744.     class WanderGoal extends Goal {
  745.  
  746.         WanderGoal() {
  747.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  748.         }
  749.  
  750.         public boolean canUse() {
  751.             return Butterfly.this.navigation.isDone() && Butterfly.this.random.nextInt(10) == 0;
  752.         }
  753.  
  754.         public boolean canContinueToUse() {
  755.             return Butterfly.this.navigation.isInProgress();
  756.         }
  757.  
  758.         public void start() {
  759.             Vec3 vec3 = this.findPos();
  760.  
  761.             if (vec3 != null) {
  762.                 Butterfly.this.navigation.moveTo(Butterfly.this.navigation.createPath(BlockPos.containing(vec3), 1), 1.0D);
  763.             }
  764.         }
  765.  
  766.         @Nullable
  767.         private Vec3 findPos() {
  768.             Vec3 vec3;
  769.             if (Butterfly.this.isNestValid() && Butterfly.this.nestPos != null && !Butterfly.this.isCloserThan(Butterfly.this.nestPos, 22)) {
  770.                 Vec3 vec31 = Vec3.atCenterOf(Butterfly.this.nestPos);
  771.                 vec3 = vec31.subtract(Butterfly.this.position()).normalize();
  772.             } else {
  773.                 vec3 = Butterfly.this.getViewVector(0.0F);
  774.             }
  775.  
  776.             Vec3 vec32 = HoverRandomPos.getPos(Butterfly.this, 8, 7, vec3.x, vec3.z, ((float)Math.PI / 2F), 3, 1);
  777.             return vec32 != null ? vec32 : AirAndWaterRandomPos.getPos(Butterfly.this, 8, 4, -2, vec3.x, vec3.z, ((float)Math.PI / 2F));
  778.         }
  779.     }
  780.  
  781.     class ButterflyLookControl extends LookControl {
  782.  
  783.         ButterflyLookControl(Mob mob) {
  784.             super(mob);
  785.         }
  786.  
  787.         public void tick() {
  788.             super.tick();
  789.         }
  790.  
  791.         protected boolean resetXRotOnTick() {
  792.             return !Butterfly.this.pollinateGoal.isPollinating();
  793.         }
  794.     }
  795.  
  796.     public enum Type implements StringRepresentable {
  797.         APRICOT(0, "monarch");
  798.         //CITRUS(1, "citrus"),
  799.         //MYSTIC(2, "mystic"),
  800.         //VALENTINE(3, "valentine"),
  801.         //JELLY(4, "jelly"),
  802.         //AUGUST(5, "august"),
  803.         //MARSHMALLOW(6, "marshmallow");
  804.  
  805.         private final int id;
  806.         private final String name;
  807.  
  808.         Type(int id, String name) {
  809.             this.id = id;
  810.             this.name = name;
  811.         }
  812.  
  813.         public int getId() {
  814.             return this.id;
  815.         }
  816.  
  817.         public String getSerializedName() {
  818.             return this.name;
  819.         }
  820.  
  821.         public static Type byId(int id) {
  822.             return ByIdMap.continuous(Type::getId, values(), ByIdMap.OutOfBoundsStrategy.ZERO).apply(id);
  823.         }
  824.  
  825.         public static Type byName(String name) {
  826.             return StringRepresentable.fromEnum(Type::values).byName(name, APRICOT);
  827.         }
  828.     }
  829.  
  830. }
Add Comment
Please, Sign In to add comment