jayhillx

butterfly (pre-release)

Sep 25th, 2023
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 38.89 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.targeting.TargetingConditions;
  38. import net.minecraft.world.entity.ai.util.AirAndWaterRandomPos;
  39. import net.minecraft.world.entity.ai.util.AirRandomPos;
  40. import net.minecraft.world.entity.ai.util.HoverRandomPos;
  41. import net.minecraft.world.entity.ai.village.poi.PoiManager;
  42. import net.minecraft.world.entity.ai.village.poi.PoiRecord;
  43. import net.minecraft.world.entity.animal.Animal;
  44. import net.minecraft.world.entity.animal.FlyingAnimal;
  45. import net.minecraft.world.entity.player.Player;
  46. import net.minecraft.world.item.ItemStack;
  47. import net.minecraft.world.item.crafting.Ingredient;
  48. import net.minecraft.world.level.Level;
  49. import net.minecraft.world.level.LevelReader;
  50. import net.minecraft.world.level.ServerLevelAccessor;
  51. import net.minecraft.world.level.block.Block;
  52. import net.minecraft.world.level.block.Blocks;
  53. import net.minecraft.world.level.block.DoublePlantBlock;
  54. import net.minecraft.world.level.block.entity.BlockEntity;
  55. import net.minecraft.world.level.block.state.BlockState;
  56. import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
  57. import net.minecraft.world.level.gameevent.GameEvent;
  58. import net.minecraft.world.level.pathfinder.BlockPathTypes;
  59. import net.minecraft.world.level.pathfinder.Path;
  60. import net.minecraft.world.phys.Vec3;
  61. import org.jetbrains.annotations.Nullable;
  62.  
  63. import java.util.*;
  64. import java.util.function.Predicate;
  65. import java.util.stream.Collectors;
  66. import java.util.stream.Stream;
  67.  
  68. /**
  69.  * Butterflies are friendly ambient anthropoids, useful for growing flowers.
  70.  */
  71. public class Butterfly extends Animal implements FlyingAnimal {
  72.     private static final EntityDataAccessor<Integer> DATA_TYPE_ID = SynchedEntityData.defineId(Butterfly.class, EntityDataSerializers.INT);
  73.     private boolean sleeping;
  74.     private int ticksSinceLastSlept;
  75.     private boolean isInNest;
  76.     private int stayOutOfNestCountdown;
  77.     private int remainingTicksBeforeLocatingNewNest;
  78.     private int ticksSincePollinated; // 2400 ticks (2 minutes)
  79.     private boolean hasNectar;
  80.     private int nectarPoints;
  81.     @Nullable
  82.     private BlockPos nestPos;
  83.     @Nullable
  84.     private Block givenFlower;
  85.     Butterfly.PollinateGoal pollinateGoal;
  86.     Butterfly.SpreadFlowersGoal spreadFlowersGoal;
  87.     public Butterfly.BreedGoal breedGoal;
  88.     @Nullable
  89.     private Vec3 hoverPos;
  90.     private int underWaterTicks;
  91.     private boolean breeding;
  92.  
  93.     public final AnimationState flyingAnimationState = new AnimationState();
  94.  
  95.     public Butterfly(EntityType<? extends Butterfly> entityType, Level level) {
  96.         super(entityType, level);
  97.         this.moveControl = new FlyingMoveControl(this, 20, true);
  98.         this.lookControl = new Butterfly.ButterflyLookControl(this);
  99.         this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F);
  100.         this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
  101.         this.setPathfindingMalus(BlockPathTypes.WATER_BORDER, 16.0F);
  102.     }
  103.  
  104.     //////////////////////////////////////////////////////////////////////////////////////////
  105.  
  106.     // DATA & TYPES
  107.  
  108.     protected void defineSynchedData() {
  109.         super.defineSynchedData();
  110.         this.entityData.define(DATA_TYPE_ID, 0);
  111.     }
  112.  
  113.     protected void registerGoals() {
  114.         this.goalSelector.addGoal(0, new Butterfly.EnterNestGoal());
  115.         this.breedGoal = new Butterfly.BreedGoal();
  116.         this.goalSelector.addGoal(1, this.breedGoal);
  117.         this.spreadFlowersGoal = new Butterfly.SpreadFlowersGoal();
  118.         this.goalSelector.addGoal(2,  this.spreadFlowersGoal);
  119.         this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(ItemTags.FLOWERS), false));
  120.         this.pollinateGoal = new Butterfly.PollinateGoal();
  121.         this.goalSelector.addGoal(4, this.pollinateGoal);
  122.         this.goalSelector.addGoal(5, new Butterfly.GoToNestGoal());
  123.         this.goalSelector.addGoal(6, new Butterfly.LocateNestGoal());
  124.         this.goalSelector.addGoal(7, new Butterfly.WanderGoal());
  125.         this.goalSelector.addGoal(8, new FloatGoal(this));
  126.     }
  127.  
  128.     public static AttributeSupplier.Builder createAttributes() {
  129.         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);
  130.     }
  131.  
  132.     @Override
  133.     public void addAdditionalSaveData(CompoundTag tag) {
  134.         super.addAdditionalSaveData(tag);
  135.         tag.putString("Type", this.getVariant().getSerializedName());
  136.  
  137.         tag.putBoolean("IsSleeping", this.isSleeping());
  138.         tag.putBoolean("IsBreeding", this.isBreeding());
  139.         tag.putBoolean("HasNectar", this.hasNectar);
  140.         tag.putInt("NectarPoints", this.nectarPoints);
  141.         tag.putInt("TicksSinceLastSlept", this.ticksSinceLastSlept);
  142.         tag.putInt("TicksSincePollinated", this.ticksSincePollinated);
  143.         tag.putInt("CannotEnterNestTicks", this.stayOutOfNestCountdown);
  144.  
  145.         if (this.nestPos != null) {
  146.             tag.put("NestPos", NbtUtils.writeBlockPos(this.nestPos));
  147.         }
  148.     }
  149.  
  150.     @Override
  151.     public void readAdditionalSaveData(CompoundTag tag) {
  152.         super.readAdditionalSaveData(tag);
  153.         this.setVariant(Type.byName(tag.getString("Type")));
  154.  
  155.         this.sleeping = tag.getBoolean("IsSleeping");
  156.         this.breeding = tag.getBoolean("IsBreeding");
  157.         this.hasNectar = tag.getBoolean("HasNectar");
  158.         this.nectarPoints = tag.getInt("NectarPoints");
  159.         this.ticksSinceLastSlept = tag.getInt("TicksSinceLastSlept");
  160.         this.ticksSincePollinated = tag.getInt("TicksSincePollinated");
  161.         this.stayOutOfNestCountdown = tag.getInt("CannotEnterNestTicks");
  162.  
  163.         this.nestPos = null;
  164.         if (tag.contains("NestPos")) {
  165.             this.nestPos = NbtUtils.readBlockPos(tag.getCompound("NestPos"));
  166.         }
  167.     }
  168.  
  169.     public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
  170.         return null;
  171.     }
  172.  
  173.     public MobType getMobType() {
  174.         return MobType.ARTHROPOD;
  175.     }
  176.  
  177.     public Type getVariant() {
  178.         return Type.byId(this.entityData.get(DATA_TYPE_ID));
  179.     }
  180.  
  181.     public void setVariant(Type type) {
  182.         this.entityData.set(DATA_TYPE_ID, type.getId());
  183.     }
  184.  
  185.     //////////////////////////////////////////////////////////////////////////////////////////
  186.  
  187.     // TICK & AI STEP
  188.  
  189.     @Override
  190.     public void tick() {
  191.         super.tick();
  192.         if (this.level().isClientSide()) {
  193.             this.flyingAnimationState.animateWhen(this.isFlying(), this.tickCount);
  194.         }
  195.  
  196.         if (this.level() instanceof ServerLevel serverLevel) {
  197.             if (this.random.nextFloat() < 0.05F) {
  198.                 if (this.hasNectar()) {
  199.                     serverLevel.sendParticles(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, 0, 0.0D, 0.0D, 0.0D);
  200.                 }
  201.  
  202.                 if (this.spreadFlowersGoal != null && this.spreadFlowersGoal.isPlantingFlower()) {
  203.                     for (int i = 0; i < 5; ++i) {
  204.                         double d0 = this.random.nextGaussian() * 0.02D;
  205.                         double d1 = this.random.nextGaussian() * 0.02D;
  206.                         double d2 = this.random.nextGaussian() * 0.02D;
  207.  
  208.                         serverLevel.sendParticles(ParticleTypes.HAPPY_VILLAGER, this.getRandomX(0.5D), this.getRandomY() - 1.0D, this.getRandomZ(0.5D), 0, (float)this.level().getRandom().nextInt(4) / 24.0F, d0, d1, d2);
  209.                     }
  210.                 }
  211.             }
  212.         }
  213.     }
  214.  
  215.     @Override
  216.     public void aiStep() {
  217.         super.aiStep();
  218.         if (!this.level().isClientSide) {
  219.             if (this.stayOutOfNestCountdown > 0) {
  220.                 --this.stayOutOfNestCountdown;
  221.             }
  222.  
  223.             if (this.remainingTicksBeforeLocatingNewNest > 0) {
  224.                 --this.remainingTicksBeforeLocatingNewNest;
  225.             }
  226.  
  227.             if (this.tickCount % 20 == 0 && !this.isNestValid()) {
  228.                 this.nestPos = null;
  229.             }
  230.         }
  231.     }
  232.  
  233.     @Override
  234.     protected void customServerAiStep() {
  235.         super.customServerAiStep();
  236.         if (this.isInWaterOrBubble()) {
  237.             ++this.underWaterTicks;
  238.         } else {
  239.             this.underWaterTicks = 0;
  240.         }
  241.  
  242.         if (this.underWaterTicks > 20) {
  243.             this.hurt(this.damageSources().drown(), 1.0F);
  244.         }
  245.  
  246.         if (!this.isSleeping()) {
  247.             ++this.ticksSinceLastSlept;
  248.         }
  249.  
  250.         if (this.hasNest()) {
  251.             ++this.ticksSincePollinated;
  252.         }
  253.     }
  254.  
  255.     //////////////////////////////////////////////////////////////////////////////////////////
  256.  
  257.     // MISC
  258.  
  259.     protected void addParticlesAroundSelf(ParticleOptions options) {
  260.         for (int i = 0; i < 5; ++i) {
  261.             double d0 = this.random.nextGaussian() * 0.02D;
  262.             double d1 = this.random.nextGaussian() * 0.02D;
  263.             double d2 = this.random.nextGaussian() * 0.02D;
  264.             this.level().addParticle(options, this.getRandomX(1.0D), this.getRandomY() + 1.0D, this.getRandomZ(1.0D), d0, d1, d2);
  265.         }
  266.     }
  267.  
  268.     protected void playStepSound(BlockPos pos, BlockState state) {
  269.     }
  270.  
  271.     protected float getSoundVolume() {
  272.         return 0.4F;
  273.     }
  274.  
  275.     protected SoundEvent getAmbientSound() {
  276.         return MysticSounds.BUTTERFLY_AMBIENT.get();
  277.     }
  278.  
  279.     protected SoundEvent getHurtSound(DamageSource source) {
  280.         return MysticSounds.BUTTERFLY_HURT.get();
  281.     }
  282.  
  283.     protected SoundEvent getDeathSound() {
  284.         return MysticSounds.BUTTERFLY_DEATH.get();
  285.     }
  286.  
  287.     //////////////////////////////////////////////////////////////////////////////////////////
  288.  
  289.     // MOVEMENT
  290.  
  291.     protected PathNavigation createNavigation(Level level) {
  292.         FlyingPathNavigation navigation = new FlyingPathNavigation(this, level) {
  293.             public boolean isStableDestination(BlockPos p_27947_) {
  294.                 return !this.level.getBlockState(p_27947_.below()).isAir();
  295.             }
  296.  
  297.             public void tick() {
  298.                 if (!Butterfly.this.pollinateGoal.isPollinating() || !Butterfly.this.spreadFlowersGoal.isPlantingFlower()) {
  299.                     super.tick();
  300.                 }
  301.             }
  302.         };
  303.         navigation.setCanOpenDoors(false);
  304.         navigation.setCanFloat(false);
  305.         navigation.setCanPassDoors(true);
  306.         return navigation;
  307.     }
  308.  
  309.     protected void checkFallDamage(double distance, boolean b, BlockState state, BlockPos pos) {
  310.     }
  311.  
  312.     protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
  313.         return dimensions.height * 0.5F;
  314.     }
  315.  
  316.     public float getWalkTargetValue(BlockPos pos, LevelReader reader) {
  317.         return reader.getBlockState(pos).isAir() ? 5.0F : 0.0F;
  318.     }
  319.  
  320.     public boolean isFlying() {
  321.         return !this.onGround();
  322.     }
  323.  
  324.     //////////////////////////////////////////////////////////////////////////////////////////
  325.  
  326.     // SLEEP & NEST
  327.  
  328.     public boolean isTired() {
  329.         return this.ticksSinceLastSlept > 18000;
  330.     }
  331.  
  332.     public void setTicksSinceLastSlept(int ticks) {
  333.         this.ticksSinceLastSlept = ticks;
  334.     }
  335.  
  336.     public boolean isSleeping() {
  337.         return this.sleeping;
  338.     }
  339.  
  340.     public void setIsSleeping(boolean sleeping) {
  341.         this.sleeping = sleeping;
  342.     }
  343.  
  344.     public boolean hasNest() {
  345.         return this.nestPos != null;
  346.     }
  347.  
  348.     private boolean isInNest() {
  349.         return this.isInNest;
  350.     }
  351.  
  352.     private boolean isNestValid() {
  353.         if (this.nestPos == null) {
  354.             return false;
  355.         } else if (this.isTooFarAway(this.nestPos)) {
  356.             return false;
  357.         } else {
  358.             BlockEntity blockEntity = this.level().getBlockEntity(this.nestPos);
  359.             return blockEntity instanceof ButterflyNestBlockEntity;
  360.         }
  361.     }
  362.  
  363.     private boolean isNestNearFire() {
  364.         if (this.nestPos == null) {
  365.             return false;
  366.         } else {
  367.             BlockEntity blockEntity = this.level().getBlockEntity(this.nestPos);
  368.             return blockEntity instanceof ButterflyNestBlockEntity && ((ButterflyNestBlockEntity)blockEntity).isFireNearby();
  369.         }
  370.     }
  371.  
  372.     private boolean doesNestHaveSpace(BlockPos pos) {
  373.         BlockEntity blockEntity = this.level().getBlockEntity(pos);
  374.  
  375.         if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  376.             return !entity.isFull();
  377.         } else {
  378.             return false;
  379.         }
  380.     }
  381.  
  382.     private boolean wantsToEnterNest() {
  383.         if (this.isBreeding()) {
  384.             return true;
  385.         } else if (this.stayOutOfNestCountdown <= 0) {
  386.             boolean flag = this.isTired() || this.level().isRaining() || this.level().isNight() || this.hasNectar();
  387.             return flag && !this.isNestNearFire();
  388.         } else {
  389.             return false;
  390.         }
  391.     }
  392.  
  393.     public void setStayOutOfNestCountdown(int ticks) {
  394.         this.stayOutOfNestCountdown = ticks;
  395.     }
  396.  
  397.     //////////////////////////////////////////////////////////////////////////////////////////
  398.  
  399.     // NECTAR & FLOWERS
  400.  
  401.     public boolean isFood(ItemStack stack) {
  402.         return stack.is(ItemTags.TALL_FLOWERS);
  403.     }
  404.  
  405.     public boolean hasNectar() {
  406.         if (this.nectarPoints > 0) {
  407.             this.hasNectar = true;
  408.             return true;
  409.         } else {
  410.             return false;
  411.         }
  412.     }
  413.  
  414.     public void dropOffNectar() {
  415.         this.hasNectar = false;
  416.         this.nectarPoints = 0;
  417.     }
  418.  
  419.     public boolean canPollinate() {
  420.         return this.ticksSincePollinated >= 2400;
  421.     }
  422.  
  423.     /** Determines when a butterfly wants to pollinate after their cooldown is over. */
  424.     public boolean wantsToPollinate() {
  425.         return this.canPollinate() || !this.isTired() && !this.hasNectar() && this.stayOutOfNestCountdown <= 0;
  426.     }
  427.  
  428.     private boolean wasGivenFlower() {
  429.         return this.givenFlower != null;
  430.     }
  431.  
  432.     //////////////////////////////////////////////////////////////////////////////////////////
  433.  
  434.     public boolean isBreeding() {
  435.         return this.breeding;
  436.     }
  437.  
  438.     public void setBreeding(boolean breeding) {
  439.         this.breeding = breeding;
  440.     }
  441.  
  442.     //////////////////////////////////////////////////////////////////////////////////////////
  443.  
  444.     @Override
  445.     public InteractionResult mobInteract(Player player, InteractionHand hand) {
  446.         if (player.getItemInHand(hand).is(ItemTags.SMALL_FLOWERS)) {
  447.  
  448.             if (this.ticksSincePollinated < 2400) {
  449.                 this.addParticlesAroundSelf(ParticleTypes.ANGRY_VILLAGER);
  450.                 return InteractionResult.FAIL;
  451.             } else {
  452.                 this.givenFlower = Block.byItem(player.getItemInHand(hand).getItem());
  453.  
  454.                 this.addParticlesAroundSelf(ParticleTypes.GLOW_SQUID_INK);
  455.                 return InteractionResult.CONSUME;
  456.             }
  457.         } else {
  458.             return this.hasNest() ? super.mobInteract(player, hand) : InteractionResult.PASS;
  459.         }
  460.     }
  461.  
  462.     @Override
  463.     public SpawnGroupData finalizeSpawn(ServerLevelAccessor accessor, DifficultyInstance instance, MobSpawnType type, @Nullable SpawnGroupData data, @Nullable CompoundTag tag) {
  464.         data = super.finalizeSpawn(accessor, instance, type, data, tag);
  465.         this.setVariant(Type.byId(random.nextInt(3)));
  466.         return data;
  467.     }
  468.  
  469.     @Override
  470.     public boolean hurt(DamageSource source, float amount) {
  471.         if (this.isInvulnerableTo(source)) {
  472.             return false;
  473.         } else {
  474.             if (!this.level().isClientSide) {
  475.                 this.pollinateGoal.stopPollinating();
  476.             }
  477.             return super.hurt(source, amount);
  478.         }
  479.     }
  480.  
  481.     //////////////////////////////////////////////////////////////////////////////////////////
  482.  
  483.     // DISTANCE & POSITION
  484.  
  485.     private void pathfindRandomlyTowards(BlockPos pos) {
  486.         Vec3 vec3 = Vec3.atBottomCenterOf(pos);
  487.         int i = 0;
  488.         BlockPos pos1 = this.blockPosition();
  489.         int j = (int)vec3.y - pos1.getY();
  490.         if (j > 2) {
  491.             i = 4;
  492.         } else if (j < -2) {
  493.             i = -4;
  494.         }
  495.  
  496.         int k = 6;
  497.         int l = 8;
  498.         int i1 = pos1.distManhattan(pos);
  499.         if (i1 < 15) {
  500.             k = i1 / 2;
  501.             l = i1 / 2;
  502.         }
  503.  
  504.         Vec3 vec31 = AirRandomPos.getPosTowards(this, k, l, i, vec3, (float)Math.PI / 10F);
  505.         if (vec31 != null) {
  506.             this.navigation.setMaxVisitedNodesMultiplier(0.5F);
  507.             this.navigation.moveTo(vec31.x, vec31.y, vec31.z, 1.0D);
  508.         }
  509.     }
  510.  
  511.     private boolean pathfindDirectlyTowards(BlockPos pos, double speedModifier) {
  512.         Butterfly.this.navigation.setMaxVisitedNodesMultiplier(10.0F);
  513.         Butterfly.this.navigation.moveTo(pos.getX(), pos.getY(), pos.getZ(), speedModifier);
  514.         return Butterfly.this.navigation.getPath() != null && Butterfly.this.navigation.getPath().canReach();
  515.     }
  516.  
  517.     private void setPollinatingPos(BlockPos pos) {
  518.         Vec3 vec3 = Vec3.atBottomCenterOf(pos).add(0.0D, 0.6F, 0.0D);
  519.         if (vec3.distanceTo(this.position()) > 1.0D) {
  520.             this.hoverPos = vec3;
  521.             this.setWantedPos();
  522.         } else {
  523.             if (this.hoverPos == null) {
  524.                 this.hoverPos = vec3;
  525.             }
  526.  
  527.             boolean flag = this.position().distanceTo(this.hoverPos) <= 0.1D;
  528.             boolean flag1 = true;
  529.             if (flag) {
  530.                 boolean flag2 = this.random.nextInt(25) == 0;
  531.                 if (flag2) {
  532.                     float offset = (this.random.nextFloat() * 2.0F - 1.0F) * 0.33333334F;
  533.  
  534.                     this.hoverPos = new Vec3(vec3.x() + (double) offset, vec3.y(), vec3.z() + (double) offset);
  535.                     this.navigation.stop();
  536.                 } else {
  537.                     flag1 = false;
  538.                 }
  539.  
  540.                 this.getLookControl().setLookAt(vec3.x(), vec3.y(), vec3.z());
  541.             }
  542.  
  543.             if (flag1) {
  544.                 this.setWantedPos();
  545.             }
  546.         }
  547.     }
  548.  
  549.     private void setWantedPos() {
  550.         if (this.hoverPos != null) {
  551.             this.getMoveControl().setWantedPosition(this.hoverPos.x(), this.hoverPos.y(), this.hoverPos.z(), 0.35F);
  552.         }
  553.     }
  554.  
  555.     private boolean isCloserThan(BlockPos pos, int distance) {
  556.         return pos.closerThan(this.blockPosition(), distance);
  557.     }
  558.  
  559.     private boolean isTooFarAway(BlockPos pos) {
  560.         return !this.isCloserThan(pos, 32);
  561.     }
  562.  
  563.     private boolean hasReachedTarget(BlockPos pos) {
  564.         if (this.isCloserThan(pos, 2)) {
  565.             return true;
  566.         } else {
  567.             Path path = this.navigation.getPath();
  568.             return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
  569.         }
  570.     }
  571.  
  572.     //////////////////////////////////////////////////////////////////////////////////////////
  573.  
  574.     /**
  575.      * Sets the butterflies home/nest position by scoping out an available one nearby.
  576.      */
  577.     class LocateNestGoal extends Goal {
  578.  
  579.         public boolean canUse() {
  580.             return Butterfly.this.remainingTicksBeforeLocatingNewNest == 0 && Butterfly.this.nestPos == null && Butterfly.this.wantsToEnterNest();
  581.         }
  582.  
  583.         public boolean canContinueToUse() {
  584.             return false;
  585.         }
  586.  
  587.         public void start() {
  588.             Butterfly.this.remainingTicksBeforeLocatingNewNest = 200;
  589.  
  590.             List<BlockPos> list = this.findNearbyNestsWithSpace();
  591.             if (!list.isEmpty()) {
  592.                 for (BlockPos pos : list) {
  593.                     Butterfly.this.nestPos = pos;
  594.                     return;
  595.                 }
  596.                 Butterfly.this.nestPos = list.get(0);
  597.             }
  598.         }
  599.  
  600.         private List<BlockPos> findNearbyNestsWithSpace() {
  601.             BlockPos pos = Butterfly.this.blockPosition();
  602.             PoiManager poiManager = ((ServerLevel)Butterfly.this.level()).getPoiManager();
  603.  
  604.             Stream<PoiRecord> stream = poiManager.getInRange((holder) -> holder.is(MysticPoiTypes.BUTTERFLY_NEST.getId()), pos, 20, PoiManager.Occupancy.ANY);
  605.             return stream.map(PoiRecord::getPos).filter(Butterfly.this::doesNestHaveSpace).sorted(Comparator.comparingDouble((pos1) -> pos1.distSqr(pos))).collect(Collectors.toList());
  606.         }
  607.     }
  608.  
  609.     class GoToNestGoal extends Goal {
  610.         @Nullable
  611.         private Path lastPath;
  612.  
  613.         GoToNestGoal() {
  614.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  615.         }
  616.  
  617.         public boolean canUse() {
  618.             return Butterfly.this.nestPos != null && Butterfly.this.wantsToEnterNest() && !Butterfly.this.hasReachedTarget(Butterfly.this.nestPos) && Butterfly.this.level().getBlockState(Butterfly.this.nestPos).is(MysticBlocks.BUTTERFLY_NEST.get());
  619.         }
  620.  
  621.         public boolean canContinueToUse() {
  622.             return this.canUse();
  623.         }
  624.  
  625.         public void stop() {
  626.             Butterfly.this.navigation.stop();
  627.             Butterfly.this.navigation.resetMaxVisitedNodesMultiplier();
  628.         }
  629.  
  630.         public void tick() {
  631.             if (Butterfly.this.nestPos != null) {
  632.  
  633.                 if (!Butterfly.this.navigation.isInProgress()) {
  634.                     if (!Butterfly.this.isCloserThan(Butterfly.this.nestPos, 16)) {
  635.                         Butterfly.this.pathfindRandomlyTowards(Butterfly.this.nestPos);
  636.                     } else {
  637.                         boolean flag = Butterfly.this.pathfindDirectlyTowards(Butterfly.this.nestPos, 1.0D);
  638.  
  639.                         if (flag) {
  640.                             if (this.lastPath != null && Butterfly.this.navigation.getPath() != null && !Butterfly.this.navigation.getPath().sameAs(this.lastPath)) {
  641.                                 this.lastPath = Butterfly.this.navigation.getPath();
  642.                             }
  643.                         }
  644.                     }
  645.                 }
  646.             }
  647.         }
  648.     }
  649.  
  650.     class EnterNestGoal extends Goal {
  651.  
  652.         public boolean canUse() {
  653.             if (Butterfly.this.nestPos != null && Butterfly.this.wantsToEnterNest() && Butterfly.this.nestPos.closerToCenterThan(Butterfly.this.position(), 2.0D)) {
  654.                 BlockEntity blockEntity = Butterfly.this.level().getBlockEntity(Butterfly.this.nestPos);
  655.  
  656.                 if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  657.                     return !entity.isFull();
  658.                 }
  659.             }
  660.             return false;
  661.         }
  662.  
  663.         public boolean canContinueToUse() {
  664.             return false;
  665.         }
  666.  
  667.         public void start() {
  668.             if (Butterfly.this.level().isNight()) {
  669.                 Butterfly.this.setIsSleeping(true);
  670.             }
  671.  
  672.             Butterfly.this.isInNest = true;
  673.  
  674.             if (Butterfly.this.nestPos != null) {
  675.                 BlockEntity blockEntity = Butterfly.this.level().getBlockEntity(Butterfly.this.nestPos);
  676.  
  677.                 if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  678.                     entity.addOccupant(Butterfly.this, Butterfly.this.hasNectar(), Butterfly.this.isBreeding());
  679.                 }
  680.             }
  681.         }
  682.  
  683.         public void stop() {
  684.             Butterfly.this.isInNest = false;
  685.         }
  686.     }
  687.  
  688.     //////////////////////////////////////////////////////////////////////////////////////////
  689.  
  690.     /**
  691.      * Either pollinates a random flower or locates and pollinates the same flower a player gave them.
  692.      */
  693.     class PollinateGoal extends Goal {
  694.         private int pollinatingTicks;
  695.         private boolean pollinating;
  696.         private boolean willSpreadFlowersAfter;
  697.         @Nullable
  698.         private BlockPos flowerPos;
  699.         private final Predicate<BlockState> VALID_POLLINATION_BLOCKS = (state) -> {
  700.             if (state.is(BlockTags.FLOWERS)) {
  701.                 if (state.is(Blocks.SUNFLOWER)) {
  702.                     return state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER;
  703.                 } else {
  704.                     return true;
  705.                 }
  706.             } else {
  707.                 return false;
  708.             }
  709.         };
  710.  
  711.         PollinateGoal() {
  712.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  713.         }
  714.  
  715.         public boolean canUse() {
  716.             if (!Butterfly.this.hasNest()) {
  717.                 return false;
  718.             } else if (Butterfly.this.hasNectar()) {
  719.                 return false;
  720.             } else if (Butterfly.this.level().isRaining()) {
  721.                 return false;
  722.             } else {
  723.                 if (Butterfly.this.canPollinate()) {
  724.                     return Butterfly.this.wasGivenFlower() || Butterfly.this.wantsToPollinate();
  725.                 } else {
  726.                     return false;
  727.                 }
  728.             }
  729.         }
  730.  
  731.         public boolean canContinueToUse() {
  732.             if (Butterfly.this.level().isRaining()) {
  733.                 return false;
  734.             } else {
  735.                 return this.flowerPos != null && !(this.pollinatingTicks > 600);
  736.             }
  737.         }
  738.  
  739.         public void start() {
  740.             this.pollinatingTicks = 0;
  741.  
  742.             Optional<BlockPos> optional = this.findNearbyFlower();
  743.             if (optional.isPresent()) {
  744.                 this.flowerPos = optional.get();
  745.             }
  746.         }
  747.  
  748.         public void stop() {
  749.             this.pollinating = false;
  750.  
  751.             if (this.pollinatingTicks > 600) {
  752.                 Butterfly.this.nectarPoints += 3;
  753.             }
  754.  
  755.             if (!Butterfly.this.wasGivenFlower()) {
  756.                 this.flowerPos = null;
  757.                 Butterfly.this.navigation.stop();
  758.                 Butterfly.this.ticksSincePollinated = 0; // 2 minute cooldown
  759.             } else {
  760.                 this.willSpreadFlowersAfter = true;
  761.             }
  762.         }
  763.  
  764.         public boolean requiresUpdateEveryTick() {
  765.             return true;
  766.         }
  767.  
  768.         public void tick() {
  769.             Optional<BlockPos> optional = this.findNearbyFlower();
  770.             if (optional.isEmpty()) {
  771.                 this.flowerPos = null;
  772.                 Butterfly.this.ticksSincePollinated = 2200;
  773.             }
  774.  
  775.             if (this.flowerPos != null) {
  776.                 if (!Butterfly.this.hasReachedTarget(this.flowerPos)) {
  777.                     Butterfly.this.pathfindDirectlyTowards(this.flowerPos, 1.0D);
  778.                 } else {
  779.                     this.pollinating = true;
  780.  
  781.                     ++this.pollinatingTicks;
  782.                     Butterfly.this.setPollinatingPos(this.flowerPos);
  783.                 }
  784.             }
  785.         }
  786.  
  787.         private Optional<BlockPos> findNearbyFlower() {
  788.             return Butterfly.this.givenFlower != null ? this.findNearestBlock((block) -> block.is(Butterfly.this.givenFlower), 16.0D) : this.findNearestBlock(this.VALID_POLLINATION_BLOCKS, 12.0D);
  789.         }
  790.  
  791.         private Optional<BlockPos> findNearestBlock(Predicate<BlockState> predicate, double distance) {
  792.             BlockPos pos = Butterfly.this.blockPosition();
  793.             BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
  794.  
  795.             for (int i = 0; (double)i <= distance; i = i > 0 ? -i : 1 - i) {
  796.                 for (int j = 0; (double)j < distance; ++j) {
  797.                     for (int k = 0; k <= j; k = k > 0 ? -k : 1 - k) {
  798.                         for (int l = k < j && k > -j ? j : 0; l <= j; l = l > 0 ? -l : 1 - l) {
  799.                             mutablePos.setWithOffset(pos, k, i - 1, l);
  800.  
  801.                             if (pos.closerThan(mutablePos, distance) && predicate.test(Butterfly.this.level().getBlockState(mutablePos))) {
  802.                                 return Optional.of(mutablePos);
  803.                             }
  804.                         }
  805.                     }
  806.                 }
  807.             }
  808.             return Optional.empty();
  809.         }
  810.  
  811.         public boolean isPollinating() {
  812.             return this.pollinating;
  813.         }
  814.  
  815.         public void stopPollinating() {
  816.             this.pollinating = false;
  817.             this.flowerPos = null;
  818.             Butterfly.this.givenFlower = null;
  819.             Butterfly.this.ticksSincePollinated = 0;
  820.         }
  821.     }
  822.  
  823.     //////////////////////////////////////////////////////////////////////////////////////////
  824.  
  825.     /**
  826.      * The main feature of butterflies; they plant 2 flowers for every 1 flower they collect nectar from.
  827.      */
  828.     class SpreadFlowersGoal extends Goal {
  829.         private int spreadFlowerTicks;
  830.         private boolean plantingFlower;
  831.         @Nullable
  832.         private BlockPos emptyPos;
  833.        
  834.         SpreadFlowersGoal() {
  835.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  836.         }
  837.  
  838.         public boolean canUse() {
  839.             return Butterfly.this.pollinateGoal.willSpreadFlowersAfter;
  840.         }
  841.  
  842.         public boolean canContinueToUse() {
  843.             return Butterfly.this.hasNectar() && Butterfly.this.wasGivenFlower() && this.spreadFlowerTicks < 1200; // 1 minute
  844.         }
  845.  
  846.         public void start() {
  847.             this.spreadFlowerTicks = 0;
  848.             this.emptyPos = this.findRandomEmptyPos();
  849.         }
  850.  
  851.         public void stop() {
  852.             this.plantingFlower = false;
  853.  
  854.             if (!Butterfly.this.level().isClientSide) {
  855.                 if (this.emptyPos != null) {
  856.                     if (Butterfly.this.givenFlower != null) {
  857.                         BlockState flowerState = Butterfly.this.givenFlower.defaultBlockState();
  858.  
  859.                         if (Butterfly.this.level().isEmptyBlock(this.emptyPos) && flowerState.canSurvive(Butterfly.this.level(), this.emptyPos)) {
  860.                             Butterfly.this.level().setBlockAndUpdate(this.emptyPos, flowerState);
  861.                             Butterfly.this.level().gameEvent(GameEvent.BLOCK_PLACE, this.emptyPos, GameEvent.Context.of(Butterfly.this, flowerState));
  862.                         }
  863.  
  864.                         Butterfly.this.nectarPoints -= 1;
  865.                         this.emptyPos = null;
  866.                     }
  867.                 }
  868.             }
  869.  
  870.             if (Butterfly.this.nectarPoints == 0) {
  871.                 Butterfly.this.givenFlower = null;
  872.                 Butterfly.this.pollinateGoal.flowerPos = null;
  873.                 Butterfly.this.pollinateGoal.willSpreadFlowersAfter = false;
  874.  
  875.                 Butterfly.this.navigation.stop();
  876.                 Butterfly.this.ticksSincePollinated = 0; // 2 minute cooldown
  877.             }
  878.         }
  879.  
  880.         public void tick() {
  881.             if (this.emptyPos != null) {
  882.                 if (!Butterfly.this.hasReachedTarget(this.emptyPos)) {
  883.                     Butterfly.this.pathfindDirectlyTowards(this.emptyPos, 0.4D);
  884.                 } else {
  885.                     this.plantingFlower = true;
  886.  
  887.                     ++this.spreadFlowerTicks;
  888.                     Butterfly.this.setPollinatingPos(this.emptyPos);
  889.                 }
  890.             } else {
  891.                 this.emptyPos = this.findRandomEmptyPos();
  892.             }
  893.         }
  894.  
  895.         /**
  896.          * @return a random empty space near the original flower pos a butterfly can plant a flower at.
  897.          */
  898.         private BlockPos findRandomEmptyPos() {
  899.             Map<Integer, BlockPos> map = Maps.newHashMap();
  900.  
  901.             BlockPos flowerPos = Butterfly.this.pollinateGoal.flowerPos;
  902.             BlockPos currentPos = flowerPos != null ? flowerPos : Butterfly.this.blockPosition();
  903.  
  904.             for (int i = 0; i <= 14; i++) {
  905.                 BlockPos pos;
  906.                 int x = Mth.floor(currentPos.getX() + (random.nextBoolean() ? random.nextInt(3) : -random.nextInt(3)));
  907.                 int y = Mth.floor(currentPos.getY());
  908.                 int z = Mth.floor(currentPos.getZ() + (random.nextBoolean() ? random.nextInt(3) : -random.nextInt(3)));
  909.                 pos = new BlockPos(x, y, z);
  910.  
  911.                 if (Butterfly.this.level().getBlockState(pos).isAir() && Butterfly.this.level().getBlockState(pos.below()).is(Blocks.GRASS_BLOCK)) {
  912.                     map.put(i, pos);
  913.                 }
  914.             }
  915.             return map.get(0);
  916.         }
  917.  
  918.         public boolean isPlantingFlower() {
  919.             return this.plantingFlower;
  920.         }
  921.     }
  922.  
  923.     //////////////////////////////////////////////////////////////////////////////////////////
  924.  
  925.     public class BreedGoal extends Goal {
  926.         private int loveTime;
  927.         @Nullable
  928.         protected Butterfly partner;
  929.  
  930.         BreedGoal() {
  931.             this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
  932.         }
  933.  
  934.         public boolean canUse() {
  935.             if (!Butterfly.this.isInLove()) {
  936.                 return false;
  937.             } else {
  938.                 Butterfly.this.breeding = true;
  939.  
  940.                 this.partner = this.getNearbyPartner();
  941.                 return this.partner != null;
  942.             }
  943.         }
  944.  
  945.         public boolean canContinueToUse() {
  946.             return Butterfly.this.breeding && this.partner != null && this.partner.isAlive() && this.partner.isInLove() && this.loveTime < 400;
  947.         }
  948.  
  949.         public void stop() {
  950.             this.partner = null;
  951.             this.loveTime = 0;
  952.         }
  953.  
  954.         public void tick() {
  955.             if (this.partner != null) {
  956.                 if (Butterfly.this.nestPos != null) {
  957.                     if (!Butterfly.this.hasReachedTarget(Butterfly.this.nestPos)) {
  958.                         Butterfly.this.pathfindDirectlyTowards(Butterfly.this.nestPos, 1.0D);
  959.                     } else {
  960.                         if (Butterfly.this.isInNest()) {
  961.                             ++this.loveTime;
  962.  
  963.                             if (this.loveTime >= this.adjustedTickDelay(400)) {
  964.                                 Butterfly.this.setBreeding(false);
  965.                             }
  966.                         }
  967.                     }
  968.                 }
  969.             }
  970.         }
  971.  
  972.         @Nullable
  973.         private Butterfly getNearbyPartner() {
  974.             List<Butterfly> list = Butterfly.this.level().getNearbyEntities(Butterfly.class, TargetingConditions.forNonCombat().range(8.0D).ignoreLineOfSight(), Butterfly.this, Butterfly.this.getBoundingBox().inflate(8.0D));
  975.             double distance = Double.MAX_VALUE;
  976.  
  977.             Butterfly partner = null;
  978.             for (Butterfly butterflies : list) {
  979.                 if (Butterfly.this.canMate(butterflies) && Butterfly.this.distanceToSqr(butterflies) < distance) {
  980.                     partner = butterflies;
  981.                     distance = Butterfly.this.distanceToSqr(butterflies);
  982.                 }
  983.             }
  984.             return partner;
  985.         }
  986.     }
  987.  
  988.     //////////////////////////////////////////////////////////////////////////////////////////
  989.  
  990.     class WanderGoal extends Goal {
  991.  
  992.         WanderGoal() {
  993.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  994.         }
  995.  
  996.         public boolean canUse() {
  997.             return Butterfly.this.navigation.isDone() && Butterfly.this.random.nextInt(10) == 0;
  998.         }
  999.  
  1000.         public boolean canContinueToUse() {
  1001.             return Butterfly.this.navigation.isInProgress();
  1002.         }
  1003.  
  1004.         public void start() {
  1005.             Vec3 vec3 = this.findPos();
  1006.  
  1007.             if (vec3 != null) {
  1008.                 Butterfly.this.navigation.moveTo(Butterfly.this.navigation.createPath(BlockPos.containing(vec3), 1), 1.0D);
  1009.             }
  1010.         }
  1011.  
  1012.         @Nullable
  1013.         private Vec3 findPos() {
  1014.             Vec3 vec3;
  1015.             if (Butterfly.this.isNestValid() && Butterfly.this.nestPos != null && !Butterfly.this.isCloserThan(Butterfly.this.nestPos, 22)) {
  1016.                 Vec3 vec31 = Vec3.atCenterOf(Butterfly.this.nestPos);
  1017.                 vec3 = vec31.subtract(Butterfly.this.position()).normalize();
  1018.             } else {
  1019.                 vec3 = Butterfly.this.getViewVector(0.0F);
  1020.             }
  1021.  
  1022.             Vec3 vec32 = HoverRandomPos.getPos(Butterfly.this, 8, 7, vec3.x, vec3.z, ((float)Math.PI / 2F), 3, 1);
  1023.             return vec32 != null ? vec32 : AirAndWaterRandomPos.getPos(Butterfly.this, 8, 4, -2, vec3.x, vec3.z, ((float)Math.PI / 2F));
  1024.         }
  1025.     }
  1026.  
  1027.     class ButterflyLookControl extends LookControl {
  1028.  
  1029.         ButterflyLookControl(Mob mob) {
  1030.             super(mob);
  1031.         }
  1032.  
  1033.         public void tick() {
  1034.             super.tick();
  1035.         }
  1036.  
  1037.         protected boolean resetXRotOnTick() {
  1038.             return !Butterfly.this.pollinateGoal.isPollinating() || !Butterfly.this.spreadFlowersGoal.isPlantingFlower();
  1039.         }
  1040.     }
  1041.  
  1042.     //////////////////////////////////////////////////////////////////////////////////////////
  1043.  
  1044.     public enum Type implements StringRepresentable {
  1045.         APRICOT(0, "apricot"),
  1046.         JELLY(1, "jelly");
  1047.  
  1048.         private final int id;
  1049.         private final String name;
  1050.  
  1051.         Type(int id, String name) {
  1052.             this.id = id;
  1053.             this.name = name;
  1054.         }
  1055.  
  1056.         public int getId() {
  1057.             return this.id;
  1058.         }
  1059.  
  1060.         public String getSerializedName() {
  1061.             return this.name;
  1062.         }
  1063.  
  1064.         public static Type byId(int id) {
  1065.             return ByIdMap.continuous(Type::getId, values(), ByIdMap.OutOfBoundsStrategy.ZERO).apply(id);
  1066.         }
  1067.  
  1068.         public static Type byName(String name) {
  1069.             return StringRepresentable.fromEnum(Type::values).byName(name, APRICOT);
  1070.         }
  1071.     }
  1072.  
  1073. }
Add Comment
Please, Sign In to add comment