Advertisement
jayhillx

butterfly

Mar 1st, 2023
947
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 19.41 KB | None | 0 0
  1. package com.mysticsbiomes.client.entity;
  2.  
  3. import com.google.common.collect.Maps;
  4. import com.mysticsbiomes.core.init.MysticPoiTypes;
  5. import com.mysticsbiomes.common.block.entity.ButterflyNestBlockEntity;
  6. import com.mysticsbiomes.core.MysticsBiomes;
  7. import com.mysticsbiomes.core.init.MysticBlocks;
  8. import net.minecraft.Util;
  9. import net.minecraft.core.BlockPos;
  10. import net.minecraft.nbt.CompoundTag;
  11. import net.minecraft.nbt.NbtUtils;
  12. import net.minecraft.network.syncher.EntityDataAccessor;
  13. import net.minecraft.network.syncher.EntityDataSerializers;
  14. import net.minecraft.network.syncher.SynchedEntityData;
  15. import net.minecraft.resources.ResourceLocation;
  16. import net.minecraft.server.level.ServerLevel;
  17. import net.minecraft.tags.ItemTags;
  18. import net.minecraft.util.Mth;
  19. import net.minecraft.world.damagesource.DamageSource;
  20. import net.minecraft.world.entity.*;
  21. import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
  22. import net.minecraft.world.entity.ai.attributes.Attributes;
  23. import net.minecraft.world.entity.ai.control.FlyingMoveControl;
  24. import net.minecraft.world.entity.ai.control.LookControl;
  25. import net.minecraft.world.entity.ai.goal.BreedGoal;
  26. import net.minecraft.world.entity.ai.goal.FloatGoal;
  27. import net.minecraft.world.entity.ai.goal.Goal;
  28. import net.minecraft.world.entity.ai.goal.TemptGoal;
  29. import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
  30. import net.minecraft.world.entity.ai.navigation.PathNavigation;
  31. import net.minecraft.world.entity.ai.util.AirAndWaterRandomPos;
  32. import net.minecraft.world.entity.ai.util.AirRandomPos;
  33. import net.minecraft.world.entity.ai.util.HoverRandomPos;
  34. import net.minecraft.world.entity.ai.village.poi.PoiManager;
  35. import net.minecraft.world.entity.ai.village.poi.PoiRecord;
  36. import net.minecraft.world.entity.animal.Animal;
  37. import net.minecraft.world.entity.animal.FlyingAnimal;
  38. import net.minecraft.world.item.crafting.Ingredient;
  39. import net.minecraft.world.level.Level;
  40. import net.minecraft.world.level.LevelReader;
  41. import net.minecraft.world.level.block.entity.BlockEntity;
  42. import net.minecraft.world.level.pathfinder.Path;
  43. import net.minecraft.world.phys.Vec3;
  44.  
  45. import javax.annotation.Nullable;
  46. import java.util.Comparator;
  47. import java.util.EnumSet;
  48. import java.util.List;
  49. import java.util.Map;
  50. import java.util.stream.Collectors;
  51. import java.util.stream.Stream;
  52.  
  53. /**
  54.  * Butterflies flutter around and spread flowers!
  55.  *
  56.  * Enter Nest
  57.  * Locate Nest
  58.  * Sleep
  59.  * Pollinate
  60.  * Locate Flowers
  61.  * Spread Flowers
  62.  *
  63.  * Bees and Butterflies work together, as butterflies collect the pollen, and bees
  64.  * turn it into honey.
  65.  *
  66.  * When they are given a flower, they will search around the area for that flower in the world.
  67.  * If they have nectar and locate that flower, they will begin to pollinate it, collecting its
  68.  * data, and duplicate the flower based on the amount of nectar they have at that time.
  69.  *
  70.  * If a butterfly has 6 pollen, they will spread out 6 flowers.
  71.  *
  72.  * If they cannot find the flower they are given, they will emmit a particle, and continue with
  73.  * their day. They will have the flower saved in their nbt. If they find the flower,
  74.  * they will start the spreading process.
  75.  *
  76.  *
  77.  * Butterflies are ambient friendly bugs, who's main feature is to spread flowers.
  78.  *
  79.  * // DAILY LIFE
  80.  *  - Butterflies flutter around in groups of 4.
  81.  *  - They will randomly decide to pollinate nearby flowers.
  82.  *      - If there are different flowers nearby, they will choose those over a flower they just pollinated.
  83.  *  - When they
  84.  *  - If butterflies do not pollinate in 2 days, they will die.
  85.  *  - Butterflies save the location of every last flower they pollinated.
  86.  *
  87.  *
  88.  *  pollinates a few times through the day
  89.  *  they flutter around
  90.  *  they drop off the nectar they collected at their nest
  91.  *  overtime the nest will have an abundance of nectar, nearby bees will try to steal some.
  92.  *
  93.  */
  94. public class Butterfly extends Animal implements FlyingAnimal {
  95.     private static final EntityDataAccessor<Integer> DATA_TYPE_ID = SynchedEntityData.defineId(Butterfly.class, EntityDataSerializers.INT);
  96.     public static final int TICKS_PER_FLAP = Mth.ceil(1.4959966F);
  97.     public static final Map<Integer, ResourceLocation> TEXTURE_BY_TYPE = Util.make(Maps.newHashMap(), (map) -> {
  98.         map.put(1, MysticsBiomes.modLoc("textures/entity/butterfly/orange.png"));
  99.         map.put(2, MysticsBiomes.modLoc("textures/entity/butterfly/peach.png"));
  100.         map.put(3, MysticsBiomes.modLoc("textures/entity/butterfly/lilac.png"));
  101.         map.put(4, MysticsBiomes.modLoc("textures/entity/butterfly/blue.png"));
  102.         map.put(5, MysticsBiomes.modLoc("textures/entity/butterfly/white.png"));
  103.         map.put(6, MysticsBiomes.modLoc("textures/entity/butterfly/black.png"));
  104.     });
  105.     private int stayOutOfNestCountdown;
  106.     int remainingCooldownBeforeLocatingNewNest;
  107.     int remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
  108.     private int underWaterTicks;
  109.     @Nullable
  110.     BlockPos nestPos;
  111.     @Nullable
  112.     BlockPos savedFlowerPos;
  113.     Butterfly.GoToNestGoal goToNestGoal;
  114.  
  115.     public Butterfly(EntityType<? extends Animal> type, Level level) {
  116.         super(type, level);
  117.         this.moveControl = new FlyingMoveControl(this, 20, true);
  118.         this.lookControl = new LookControl(this);
  119.     }
  120.  
  121.     protected void defineSynchedData() {
  122.         super.defineSynchedData();
  123.         this.entityData.define(DATA_TYPE_ID, this.random.nextInt(1, 7));
  124.     }
  125.  
  126.     protected void registerGoals() {
  127.         this.goalSelector.addGoal(1, new Butterfly.EnterNestGoal());
  128.         this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
  129.         this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(ItemTags.FLOWERS), false));
  130.         this.goalSelector.addGoal(5, new Butterfly.LocateNestGoal());
  131.         this.goToNestGoal = new Butterfly.GoToNestGoal();
  132.         this.goalSelector.addGoal(5, this.goToNestGoal);
  133.         this.goalSelector.addGoal(8, new Butterfly.WanderGoal());
  134.         this.goalSelector.addGoal(9, new FloatGoal(this));
  135.     }
  136.  
  137.     public static AttributeSupplier.Builder createAttributes() {
  138.         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, 48.0D);
  139.     }
  140.  
  141.     public void addAdditionalSaveData(CompoundTag tag) {
  142.         super.addAdditionalSaveData(tag);
  143.         tag.putInt("Type", this.getButterflyType());
  144.         tag.putInt("CannotEnterNestTicks", this.stayOutOfNestCountdown);
  145.  
  146.         if (this.hasNest()) {
  147.             tag.put("NestPos", NbtUtils.writeBlockPos(this.getNestPos()));
  148.         }
  149.     }
  150.  
  151.     public void readAdditionalSaveData(CompoundTag tag) {
  152.         super.readAdditionalSaveData(tag);
  153.         this.setButterflyType(tag.getInt("Type"));
  154.         this.stayOutOfNestCountdown = tag.getInt("CannotEnterNestTicks");
  155.  
  156.         this.nestPos = null;
  157.         if (tag.contains("NestPos")) {
  158.             this.nestPos = NbtUtils.readBlockPos(tag.getCompound("NestPos"));
  159.         }
  160.     }
  161.  
  162.     public int getButterflyType() {
  163.         return this.entityData.get(DATA_TYPE_ID);
  164.     }
  165.  
  166.     public void setButterflyType(int value) {
  167.         if (value <= 1 || value >= 6) {
  168.             value = this.random.nextInt(6);
  169.         }
  170.         this.entityData.set(DATA_TYPE_ID, value);
  171.     }
  172.  
  173.     public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
  174.         return null;
  175.     }
  176.  
  177.     ///////////////////////////////////////////////////////////////////////////////////////
  178.  
  179.     public void tick() {
  180.         super.tick();
  181.     }
  182.  
  183.     protected void customServerAiStep() {
  184.         if (this.isInWaterOrBubble()) {
  185.             ++this.underWaterTicks;
  186.         } else {
  187.             this.underWaterTicks = 0;
  188.         }
  189.  
  190.         if (this.underWaterTicks > 20) {
  191.             this.hurt(DamageSource.DROWN, 1.0F);
  192.         }
  193.     }
  194.  
  195.     public void aiStep() {
  196.         super.aiStep();
  197.         if (!this.level.isClientSide) {
  198.             if (this.stayOutOfNestCountdown > 0) {
  199.                 --this.stayOutOfNestCountdown;
  200.             }
  201.  
  202.             if (this.remainingCooldownBeforeLocatingNewNest > 0) {
  203.                 --this.remainingCooldownBeforeLocatingNewNest;
  204.             }
  205.  
  206.             if (this.remainingCooldownBeforeLocatingNewFlower > 0) {
  207.                 --this.remainingCooldownBeforeLocatingNewFlower;
  208.             }
  209.  
  210.             if (this.tickCount % 20 == 0 && !this.isNestValid()) {
  211.                 this.nestPos = null;
  212.             }
  213.         }
  214.     }
  215.  
  216.     ///////////////////////////////////////////////////////////////////////////////////////
  217.  
  218.     /**
  219.      * Butterflies cannot fly if they're wet.
  220.      */
  221.     public boolean isFlying() {
  222.         return !this.onGround || !this.isInWaterRainOrBubble();
  223.     }
  224.  
  225.     public boolean isFlapping() {
  226.         return this.isFlying() && this.tickCount % TICKS_PER_FLAP == 0;
  227.     }
  228.  
  229.     public boolean causeFallDamage(float amount, float height, DamageSource source) {
  230.         return false;
  231.     }
  232.  
  233.     public float getWalkTargetValue(BlockPos pos, LevelReader reader) {
  234.         return reader.getBlockState(pos).isAir() ? 10.0F : 0.0F;
  235.     }
  236.  
  237.     protected PathNavigation createNavigation(Level level) {
  238.         FlyingPathNavigation navigation = new FlyingPathNavigation(this, level) {
  239.             public boolean isStableDestination(BlockPos blockPos) {
  240.                 return !this.level.getBlockState(blockPos.below()).isAir();
  241.             }
  242.  
  243.             public void tick() {
  244.                 super.tick();
  245.             }
  246.         };
  247.         navigation.setCanOpenDoors(false);
  248.         navigation.setCanFloat(false);
  249.         navigation.setCanPassDoors(true);
  250.         return navigation;
  251.     }
  252.  
  253.     protected void pathfindRandomlyTowards(BlockPos blockPos) {
  254.         Vec3 vec3 = Vec3.atBottomCenterOf(blockPos);
  255.         int i = 0;
  256.         BlockPos pos = this.blockPosition();
  257.         int j = (int)vec3.y - pos.getY();
  258.         if (j > 2) {
  259.             i = 4;
  260.         } else if (j < -2) {
  261.             i = -4;
  262.         }
  263.  
  264.         int k = 6;
  265.         int l = 8;
  266.         int i1 = pos.distManhattan(blockPos);
  267.         if (i1 < 15) {
  268.             k = i1 / 2;
  269.             l = i1 / 2;
  270.         }
  271.  
  272.         Vec3 vec31 = AirRandomPos.getPosTowards(this, k, l, i, vec3, ((float)Math.PI / 10F));
  273.         if (vec31 != null) {
  274.             this.navigation.setMaxVisitedNodesMultiplier(0.5F);
  275.             this.navigation.moveTo(vec31.x, vec31.y, vec31.z, 1.0D);
  276.         }
  277.     }
  278.  
  279.     ///////////////////////////////////////////////////////////////////////////////////////
  280.  
  281.     @Nullable
  282.     public BlockPos getNestPos() {
  283.         return this.nestPos;
  284.     }
  285.  
  286.     public boolean hasNest() {
  287.         return this.nestPos != null;
  288.     }
  289.  
  290.     private boolean isNestValid() {
  291.         if (!this.hasNest()) {
  292.             return false;
  293.         } else {
  294.             BlockEntity entity = this.level.getBlockEntity(this.nestPos);
  295.             return entity instanceof ButterflyNestBlockEntity;
  296.         }
  297.     }
  298.  
  299.     private boolean isNestNearFire() {
  300.         if (this.nestPos == null) {
  301.             return false;
  302.         } else {
  303.             BlockEntity entity = this.level.getBlockEntity(this.nestPos);
  304.             return entity instanceof ButterflyNestBlockEntity && ((ButterflyNestBlockEntity)entity).isFireNearby();
  305.         }
  306.     }
  307.  
  308.     private boolean doesNestHaveSpace(BlockPos pos) {
  309.         BlockEntity entity = this.level.getBlockEntity(pos);
  310.  
  311.         if (entity instanceof ButterflyNestBlockEntity) {
  312.             return !((ButterflyNestBlockEntity)entity).isFull();
  313.         } else {
  314.             return false;
  315.         }
  316.     }
  317.  
  318.     private boolean wantsToEnterNest() {
  319.         return this.level.isRaining() || this.level.isNight() && !this.isNestNearFire();
  320.     }
  321.  
  322.     public void setStayOutOfNestCountdown(int ticks) {
  323.         this.stayOutOfNestCountdown = ticks;
  324.     }
  325.  
  326.     ///////////////////////////////////////////////////////////////////////////////////////
  327.  
  328.     @Nullable
  329.     public BlockPos getSavedFlowerPos() {
  330.         return this.savedFlowerPos;
  331.     }
  332.  
  333.     public boolean hasSavedFlowerPos() {
  334.         return this.savedFlowerPos != null;
  335.     }
  336.  
  337.     public void setSavedFlowerPos(BlockPos pos) {
  338.         this.savedFlowerPos = pos;
  339.     }
  340.  
  341.     ///////////////////////////////////////////////////////////////////////////////////////
  342.  
  343.     private boolean closerThan(BlockPos pos, int distance) {
  344.         return pos.closerThan(this.blockPosition(), distance);
  345.     }
  346.  
  347.     private boolean isTooFarAway(BlockPos pos) {
  348.         return !this.closerThan(pos, 32);
  349.     }
  350.  
  351.     ///////////////////////////////////////////////////////////////////////////////////////
  352.  
  353.     /**
  354.      * TODO:
  355.      * Enter nest goal
  356.      * Locate flower goal
  357.      * Pollinate goal
  358.      * Spread flowers goal
  359.      */
  360.     private class LocateNestGoal extends Goal {
  361.  
  362.         public boolean canUse() {
  363.             return Butterfly.this.remainingCooldownBeforeLocatingNewNest == 0 && !Butterfly.this.hasNest() && Butterfly.this.wantsToEnterNest();
  364.         }
  365.  
  366.         public boolean canContinueToUse() {
  367.             return false;
  368.         }
  369.  
  370.         public void start() {
  371.             Butterfly.this.remainingCooldownBeforeLocatingNewNest = 200;
  372.             List<BlockPos> list = this.findNearbyNestsWithSpace();
  373.  
  374.             if (!list.isEmpty()) {
  375.                 for (BlockPos blockPos : list) {
  376.                     Butterfly.this.nestPos = blockPos;
  377.                     return;
  378.                 }
  379.                 Butterfly.this.nestPos = list.get(0);
  380.             }
  381.         }
  382.  
  383.         private List<BlockPos> findNearbyNestsWithSpace() {
  384.             BlockPos blockPos = Butterfly.this.blockPosition();
  385.             PoiManager poi = ((ServerLevel)Butterfly.this.level).getPoiManager();
  386.  
  387.             Stream<PoiRecord> stream = poi.getInRange((type) -> type == MysticPoiTypes.BUTTERFLY_NEST.get(), blockPos, 20, PoiManager.Occupancy.ANY);
  388.             return stream.map(PoiRecord::getPos).filter(Butterfly.this::doesNestHaveSpace).sorted(Comparator.comparingDouble((pos) -> pos.distSqr(blockPos))).collect(Collectors.toList());
  389.         }
  390.     }
  391.  
  392.     public class GoToNestGoal extends Goal {
  393.         int travellingTicks = Butterfly.this.level.random.nextInt(10);
  394.         @Nullable
  395.         private Path lastPath;
  396.         private int ticksStuck;
  397.  
  398.         GoToNestGoal() {
  399.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  400.         }
  401.  
  402.         public boolean canUse() {
  403.             return Butterfly.this.nestPos != null && !Butterfly.this.hasRestriction() && Butterfly.this.wantsToEnterNest() && !this.hasReachedTarget(Butterfly.this.nestPos) && Butterfly.this.level.getBlockState(Butterfly.this.nestPos).is(MysticBlocks.BUTTERFLY_NEST.get());
  404.         }
  405.  
  406.         public boolean canContinueToUse() {
  407.             return this.canUse();
  408.         }
  409.  
  410.         public void start() {
  411.             this.travellingTicks = 0;
  412.             this.ticksStuck = 0;
  413.             super.start();
  414.         }
  415.  
  416.         public void stop() {
  417.             this.travellingTicks = 0;
  418.             this.ticksStuck = 0;
  419.             Butterfly.this.navigation.stop();
  420.             Butterfly.this.navigation.resetMaxVisitedNodesMultiplier();
  421.         }
  422.  
  423.         public void tick() {
  424.             if (Butterfly.this.nestPos != null) {
  425.                 ++this.travellingTicks;
  426.  
  427.                 if (!Butterfly.this.navigation.isInProgress()) {
  428.                     if (!Butterfly.this.closerThan(Butterfly.this.nestPos, 16)) {
  429.                         if (!Butterfly.this.isTooFarAway(Butterfly.this.nestPos)) {
  430.                             Butterfly.this.pathfindRandomlyTowards(Butterfly.this.nestPos);
  431.                         }
  432.                     } else {
  433.                         boolean flag = this.pathfindDirectlyTowards(Butterfly.this.nestPos);
  434.                         if (flag && this.lastPath != null && Butterfly.this.navigation.getPath().sameAs(this.lastPath)) {
  435.                             ++this.ticksStuck;
  436.                             if (this.ticksStuck > 60) {
  437.                                 this.ticksStuck = 0;
  438.                             }
  439.                         } else {
  440.                             this.lastPath = Butterfly.this.navigation.getPath();
  441.                         }
  442.                     }
  443.                 }
  444.             }
  445.         }
  446.  
  447.         private boolean pathfindDirectlyTowards(BlockPos blockPos) {
  448.             Butterfly.this.navigation.setMaxVisitedNodesMultiplier(10.0F);
  449.             Butterfly.this.navigation.moveTo(blockPos.getX(), blockPos.getY(), blockPos.getZ(), 1.0D);
  450.             return Butterfly.this.navigation.getPath() != null && Butterfly.this.navigation.getPath().canReach();
  451.         }
  452.  
  453.         private boolean hasReachedTarget(BlockPos blockPos) {
  454.             if (Butterfly.this.closerThan(blockPos, 2)) {
  455.                 return true;
  456.             } else {
  457.                 Path path = Butterfly.this.navigation.getPath();
  458.                 return path != null && path.getTarget().equals(blockPos) && path.canReach() && path.isDone();
  459.             }
  460.         }
  461.     }
  462.  
  463.     private class EnterNestGoal extends Goal {
  464.         public boolean canUse() {
  465.             if (Butterfly.this.hasNest() && Butterfly.this.wantsToEnterNest() && Butterfly.this.nestPos.closerToCenterThan(Butterfly.this.position(), 2.0D)) {
  466.                 BlockEntity blockEntity = Butterfly.this.level.getBlockEntity(Butterfly.this.nestPos);
  467.  
  468.                 if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  469.                     if (!entity.isFull()) {
  470.                         return true;
  471.                     }
  472.                     Butterfly.this.nestPos = null;
  473.                 }
  474.             }
  475.             return false;
  476.         }
  477.  
  478.         public boolean canContinueToUse() {
  479.             return false;
  480.         }
  481.  
  482.         public void start() {
  483.             BlockEntity blockEntity = Butterfly.this.level.getBlockEntity(Butterfly.this.nestPos);
  484.  
  485.             if (blockEntity instanceof ButterflyNestBlockEntity entity) {
  486.                 entity.addOccupant(Butterfly.this, Butterfly.this.hasNest());
  487.             }
  488.         }
  489.     }
  490.  
  491.     private class WanderGoal extends Goal {
  492.  
  493.         WanderGoal() {
  494.             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
  495.         }
  496.  
  497.         public boolean canUse() {
  498.             return Butterfly.this.navigation.isDone() && Butterfly.this.random.nextInt(10) == 0;
  499.         }
  500.  
  501.         public boolean canContinueToUse() {
  502.             return Butterfly.this.navigation.isInProgress();
  503.         }
  504.  
  505.         public void start() {
  506.             Vec3 vec3 = this.findPos();
  507.             if (vec3 != null) {
  508.                 Butterfly.this.navigation.moveTo(Butterfly.this.navigation.createPath(new BlockPos(vec3), 1), 1.0D);
  509.             }
  510.         }
  511.  
  512.         private Vec3 findPos() {
  513.             Vec3 vec3;
  514.             if (Butterfly.this.isNestValid() && !Butterfly.this.closerThan(Butterfly.this.nestPos, 22)) {
  515.                 Vec3 vec31 = Vec3.atCenterOf(Butterfly.this.nestPos);
  516.                 vec3 = vec31.subtract(Butterfly.this.position()).normalize();
  517.             } else {
  518.                 vec3 = Butterfly.this.getViewVector(0.0F);
  519.             }
  520.  
  521.             Vec3 vec32 = HoverRandomPos.getPos(Butterfly.this, 8, 7, vec3.x, vec3.z, ((float)Math.PI / 2F), 3, 1);
  522.             return vec32 != null ? vec32 : AirAndWaterRandomPos.getPos(Butterfly.this, 8, 4, -2, vec3.x, vec3.z, ((float)Math.PI / 2F));
  523.         }
  524.     }
  525.  
  526. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement