Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package com.mysticsbiomes.common.entity.animal.butterfly;
- import com.google.common.collect.Maps;
- import com.mysticsbiomes.common.block.entity.ButterflyNestBlockEntity;
- import com.mysticsbiomes.init.MysticBlocks;
- import com.mysticsbiomes.init.MysticPoiTypes;
- import com.mysticsbiomes.init.MysticSounds;
- import net.minecraft.core.BlockPos;
- import net.minecraft.core.particles.ParticleOptions;
- import net.minecraft.core.particles.ParticleTypes;
- import net.minecraft.nbt.CompoundTag;
- import net.minecraft.nbt.NbtUtils;
- import net.minecraft.network.syncher.EntityDataAccessor;
- import net.minecraft.network.syncher.EntityDataSerializers;
- import net.minecraft.network.syncher.SynchedEntityData;
- import net.minecraft.server.level.ServerLevel;
- import net.minecraft.sounds.SoundEvent;
- import net.minecraft.tags.BlockTags;
- import net.minecraft.tags.ItemTags;
- import net.minecraft.util.ByIdMap;
- import net.minecraft.util.Mth;
- import net.minecraft.util.StringRepresentable;
- import net.minecraft.world.DifficultyInstance;
- import net.minecraft.world.InteractionHand;
- import net.minecraft.world.InteractionResult;
- import net.minecraft.world.damagesource.DamageSource;
- import net.minecraft.world.entity.*;
- import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
- import net.minecraft.world.entity.ai.attributes.Attributes;
- import net.minecraft.world.entity.ai.control.FlyingMoveControl;
- import net.minecraft.world.entity.ai.control.LookControl;
- import net.minecraft.world.entity.ai.goal.FloatGoal;
- import net.minecraft.world.entity.ai.goal.Goal;
- import net.minecraft.world.entity.ai.goal.TemptGoal;
- import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
- import net.minecraft.world.entity.ai.navigation.PathNavigation;
- import net.minecraft.world.entity.ai.targeting.TargetingConditions;
- import net.minecraft.world.entity.ai.util.AirAndWaterRandomPos;
- import net.minecraft.world.entity.ai.util.AirRandomPos;
- import net.minecraft.world.entity.ai.util.HoverRandomPos;
- import net.minecraft.world.entity.ai.village.poi.PoiManager;
- import net.minecraft.world.entity.ai.village.poi.PoiRecord;
- import net.minecraft.world.entity.animal.Animal;
- import net.minecraft.world.entity.animal.FlyingAnimal;
- import net.minecraft.world.entity.player.Player;
- import net.minecraft.world.item.ItemStack;
- import net.minecraft.world.item.crafting.Ingredient;
- import net.minecraft.world.level.Level;
- import net.minecraft.world.level.LevelReader;
- import net.minecraft.world.level.ServerLevelAccessor;
- import net.minecraft.world.level.block.Block;
- import net.minecraft.world.level.block.Blocks;
- import net.minecraft.world.level.block.DoublePlantBlock;
- import net.minecraft.world.level.block.entity.BlockEntity;
- import net.minecraft.world.level.block.state.BlockState;
- import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
- import net.minecraft.world.level.gameevent.GameEvent;
- import net.minecraft.world.level.pathfinder.BlockPathTypes;
- import net.minecraft.world.level.pathfinder.Path;
- import net.minecraft.world.phys.Vec3;
- import org.jetbrains.annotations.Nullable;
- import java.util.*;
- import java.util.function.Predicate;
- import java.util.stream.Collectors;
- import java.util.stream.Stream;
- /**
- * Butterflies are friendly ambient anthropoids, useful for growing flowers.
- */
- public class Butterfly extends Animal implements FlyingAnimal {
- private static final EntityDataAccessor<Integer> DATA_TYPE_ID = SynchedEntityData.defineId(Butterfly.class, EntityDataSerializers.INT);
- private boolean sleeping;
- private int ticksSinceLastSlept;
- private boolean isInNest;
- private int stayOutOfNestCountdown;
- private int remainingTicksBeforeLocatingNewNest;
- private int ticksSincePollinated; // 2400 ticks (2 minutes)
- private boolean hasNectar;
- private int nectarPoints;
- @Nullable
- private BlockPos nestPos;
- @Nullable
- private Block givenFlower;
- Butterfly.PollinateGoal pollinateGoal;
- Butterfly.SpreadFlowersGoal spreadFlowersGoal;
- public Butterfly.BreedGoal breedGoal;
- @Nullable
- private Vec3 hoverPos;
- private int underWaterTicks;
- private boolean breeding;
- public final AnimationState flyingAnimationState = new AnimationState();
- public Butterfly(EntityType<? extends Butterfly> entityType, Level level) {
- super(entityType, level);
- this.moveControl = new FlyingMoveControl(this, 20, true);
- this.lookControl = new Butterfly.ButterflyLookControl(this);
- this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F);
- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
- this.setPathfindingMalus(BlockPathTypes.WATER_BORDER, 16.0F);
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // DATA & TYPES
- protected void defineSynchedData() {
- super.defineSynchedData();
- this.entityData.define(DATA_TYPE_ID, 0);
- }
- protected void registerGoals() {
- this.goalSelector.addGoal(0, new Butterfly.EnterNestGoal());
- this.breedGoal = new Butterfly.BreedGoal();
- this.goalSelector.addGoal(1, this.breedGoal);
- this.spreadFlowersGoal = new Butterfly.SpreadFlowersGoal();
- this.goalSelector.addGoal(2, this.spreadFlowersGoal);
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(ItemTags.FLOWERS), false));
- this.pollinateGoal = new Butterfly.PollinateGoal();
- this.goalSelector.addGoal(4, this.pollinateGoal);
- this.goalSelector.addGoal(5, new Butterfly.GoToNestGoal());
- this.goalSelector.addGoal(6, new Butterfly.LocateNestGoal());
- this.goalSelector.addGoal(7, new Butterfly.WanderGoal());
- this.goalSelector.addGoal(8, new FloatGoal(this));
- }
- public static AttributeSupplier.Builder createAttributes() {
- 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);
- }
- @Override
- public void addAdditionalSaveData(CompoundTag tag) {
- super.addAdditionalSaveData(tag);
- tag.putString("Type", this.getVariant().getSerializedName());
- tag.putBoolean("IsSleeping", this.isSleeping());
- tag.putBoolean("IsBreeding", this.isBreeding());
- tag.putBoolean("HasNectar", this.hasNectar);
- tag.putInt("NectarPoints", this.nectarPoints);
- tag.putInt("TicksSinceLastSlept", this.ticksSinceLastSlept);
- tag.putInt("TicksSincePollinated", this.ticksSincePollinated);
- tag.putInt("CannotEnterNestTicks", this.stayOutOfNestCountdown);
- if (this.nestPos != null) {
- tag.put("NestPos", NbtUtils.writeBlockPos(this.nestPos));
- }
- }
- @Override
- public void readAdditionalSaveData(CompoundTag tag) {
- super.readAdditionalSaveData(tag);
- this.setVariant(Type.byName(tag.getString("Type")));
- this.sleeping = tag.getBoolean("IsSleeping");
- this.breeding = tag.getBoolean("IsBreeding");
- this.hasNectar = tag.getBoolean("HasNectar");
- this.nectarPoints = tag.getInt("NectarPoints");
- this.ticksSinceLastSlept = tag.getInt("TicksSinceLastSlept");
- this.ticksSincePollinated = tag.getInt("TicksSincePollinated");
- this.stayOutOfNestCountdown = tag.getInt("CannotEnterNestTicks");
- this.nestPos = null;
- if (tag.contains("NestPos")) {
- this.nestPos = NbtUtils.readBlockPos(tag.getCompound("NestPos"));
- }
- }
- public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob mob) {
- return null;
- }
- public MobType getMobType() {
- return MobType.ARTHROPOD;
- }
- public Type getVariant() {
- return Type.byId(this.entityData.get(DATA_TYPE_ID));
- }
- public void setVariant(Type type) {
- this.entityData.set(DATA_TYPE_ID, type.getId());
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // TICK & AI STEP
- @Override
- public void tick() {
- super.tick();
- if (this.level().isClientSide()) {
- this.flyingAnimationState.animateWhen(this.isFlying(), this.tickCount);
- }
- if (this.level() instanceof ServerLevel serverLevel) {
- if (this.random.nextFloat() < 0.05F) {
- if (this.hasNectar()) {
- 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);
- }
- if (this.spreadFlowersGoal != null && this.spreadFlowersGoal.isPlantingFlower()) {
- for (int i = 0; i < 5; ++i) {
- double d0 = this.random.nextGaussian() * 0.02D;
- double d1 = this.random.nextGaussian() * 0.02D;
- double d2 = this.random.nextGaussian() * 0.02D;
- 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);
- }
- }
- }
- }
- }
- @Override
- public void aiStep() {
- super.aiStep();
- if (!this.level().isClientSide) {
- if (this.stayOutOfNestCountdown > 0) {
- --this.stayOutOfNestCountdown;
- }
- if (this.remainingTicksBeforeLocatingNewNest > 0) {
- --this.remainingTicksBeforeLocatingNewNest;
- }
- if (this.tickCount % 20 == 0 && !this.isNestValid()) {
- this.nestPos = null;
- }
- }
- }
- @Override
- protected void customServerAiStep() {
- super.customServerAiStep();
- if (this.isInWaterOrBubble()) {
- ++this.underWaterTicks;
- } else {
- this.underWaterTicks = 0;
- }
- if (this.underWaterTicks > 20) {
- this.hurt(this.damageSources().drown(), 1.0F);
- }
- if (!this.isSleeping()) {
- ++this.ticksSinceLastSlept;
- }
- if (this.hasNest()) {
- ++this.ticksSincePollinated;
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // MISC
- protected void addParticlesAroundSelf(ParticleOptions options) {
- for (int i = 0; i < 5; ++i) {
- double d0 = this.random.nextGaussian() * 0.02D;
- double d1 = this.random.nextGaussian() * 0.02D;
- double d2 = this.random.nextGaussian() * 0.02D;
- this.level().addParticle(options, this.getRandomX(1.0D), this.getRandomY() + 1.0D, this.getRandomZ(1.0D), d0, d1, d2);
- }
- }
- protected void playStepSound(BlockPos pos, BlockState state) {
- }
- protected float getSoundVolume() {
- return 0.4F;
- }
- protected SoundEvent getAmbientSound() {
- return MysticSounds.BUTTERFLY_AMBIENT.get();
- }
- protected SoundEvent getHurtSound(DamageSource source) {
- return MysticSounds.BUTTERFLY_HURT.get();
- }
- protected SoundEvent getDeathSound() {
- return MysticSounds.BUTTERFLY_DEATH.get();
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // MOVEMENT
- protected PathNavigation createNavigation(Level level) {
- FlyingPathNavigation navigation = new FlyingPathNavigation(this, level) {
- public boolean isStableDestination(BlockPos p_27947_) {
- return !this.level.getBlockState(p_27947_.below()).isAir();
- }
- public void tick() {
- if (!Butterfly.this.pollinateGoal.isPollinating() || !Butterfly.this.spreadFlowersGoal.isPlantingFlower()) {
- super.tick();
- }
- }
- };
- navigation.setCanOpenDoors(false);
- navigation.setCanFloat(false);
- navigation.setCanPassDoors(true);
- return navigation;
- }
- protected void checkFallDamage(double distance, boolean b, BlockState state, BlockPos pos) {
- }
- protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
- return dimensions.height * 0.5F;
- }
- public float getWalkTargetValue(BlockPos pos, LevelReader reader) {
- return reader.getBlockState(pos).isAir() ? 5.0F : 0.0F;
- }
- public boolean isFlying() {
- return !this.onGround();
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // SLEEP & NEST
- public boolean isTired() {
- return this.ticksSinceLastSlept > 18000;
- }
- public void setTicksSinceLastSlept(int ticks) {
- this.ticksSinceLastSlept = ticks;
- }
- public boolean isSleeping() {
- return this.sleeping;
- }
- public void setIsSleeping(boolean sleeping) {
- this.sleeping = sleeping;
- }
- public boolean hasNest() {
- return this.nestPos != null;
- }
- private boolean isInNest() {
- return this.isInNest;
- }
- private boolean isNestValid() {
- if (this.nestPos == null) {
- return false;
- } else if (this.isTooFarAway(this.nestPos)) {
- return false;
- } else {
- BlockEntity blockEntity = this.level().getBlockEntity(this.nestPos);
- return blockEntity instanceof ButterflyNestBlockEntity;
- }
- }
- private boolean isNestNearFire() {
- if (this.nestPos == null) {
- return false;
- } else {
- BlockEntity blockEntity = this.level().getBlockEntity(this.nestPos);
- return blockEntity instanceof ButterflyNestBlockEntity && ((ButterflyNestBlockEntity)blockEntity).isFireNearby();
- }
- }
- private boolean doesNestHaveSpace(BlockPos pos) {
- BlockEntity blockEntity = this.level().getBlockEntity(pos);
- if (blockEntity instanceof ButterflyNestBlockEntity entity) {
- return !entity.isFull();
- } else {
- return false;
- }
- }
- private boolean wantsToEnterNest() {
- if (this.isBreeding()) {
- return true;
- } else if (this.stayOutOfNestCountdown <= 0) {
- boolean flag = this.isTired() || this.level().isRaining() || this.level().isNight() || this.hasNectar();
- return flag && !this.isNestNearFire();
- } else {
- return false;
- }
- }
- public void setStayOutOfNestCountdown(int ticks) {
- this.stayOutOfNestCountdown = ticks;
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // NECTAR & FLOWERS
- public boolean isFood(ItemStack stack) {
- return stack.is(ItemTags.TALL_FLOWERS);
- }
- public boolean hasNectar() {
- if (this.nectarPoints > 0) {
- this.hasNectar = true;
- return true;
- } else {
- return false;
- }
- }
- public void dropOffNectar() {
- this.hasNectar = false;
- this.nectarPoints = 0;
- }
- public boolean canPollinate() {
- return this.ticksSincePollinated >= 2400;
- }
- /** Determines when a butterfly wants to pollinate after their cooldown is over. */
- public boolean wantsToPollinate() {
- return this.canPollinate() || !this.isTired() && !this.hasNectar() && this.stayOutOfNestCountdown <= 0;
- }
- private boolean wasGivenFlower() {
- return this.givenFlower != null;
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- public boolean isBreeding() {
- return this.breeding;
- }
- public void setBreeding(boolean breeding) {
- this.breeding = breeding;
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- @Override
- public InteractionResult mobInteract(Player player, InteractionHand hand) {
- if (player.getItemInHand(hand).is(ItemTags.SMALL_FLOWERS)) {
- if (this.ticksSincePollinated < 2400) {
- this.addParticlesAroundSelf(ParticleTypes.ANGRY_VILLAGER);
- return InteractionResult.FAIL;
- } else {
- this.givenFlower = Block.byItem(player.getItemInHand(hand).getItem());
- this.addParticlesAroundSelf(ParticleTypes.GLOW_SQUID_INK);
- return InteractionResult.CONSUME;
- }
- } else {
- return this.hasNest() ? super.mobInteract(player, hand) : InteractionResult.PASS;
- }
- }
- @Override
- public SpawnGroupData finalizeSpawn(ServerLevelAccessor accessor, DifficultyInstance instance, MobSpawnType type, @Nullable SpawnGroupData data, @Nullable CompoundTag tag) {
- data = super.finalizeSpawn(accessor, instance, type, data, tag);
- this.setVariant(Type.byId(random.nextInt(3)));
- return data;
- }
- @Override
- public boolean hurt(DamageSource source, float amount) {
- if (this.isInvulnerableTo(source)) {
- return false;
- } else {
- if (!this.level().isClientSide) {
- this.pollinateGoal.stopPollinating();
- }
- return super.hurt(source, amount);
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // DISTANCE & POSITION
- private void pathfindRandomlyTowards(BlockPos pos) {
- Vec3 vec3 = Vec3.atBottomCenterOf(pos);
- int i = 0;
- BlockPos pos1 = this.blockPosition();
- int j = (int)vec3.y - pos1.getY();
- if (j > 2) {
- i = 4;
- } else if (j < -2) {
- i = -4;
- }
- int k = 6;
- int l = 8;
- int i1 = pos1.distManhattan(pos);
- if (i1 < 15) {
- k = i1 / 2;
- l = i1 / 2;
- }
- Vec3 vec31 = AirRandomPos.getPosTowards(this, k, l, i, vec3, (float)Math.PI / 10F);
- if (vec31 != null) {
- this.navigation.setMaxVisitedNodesMultiplier(0.5F);
- this.navigation.moveTo(vec31.x, vec31.y, vec31.z, 1.0D);
- }
- }
- private boolean pathfindDirectlyTowards(BlockPos pos, double speedModifier) {
- Butterfly.this.navigation.setMaxVisitedNodesMultiplier(10.0F);
- Butterfly.this.navigation.moveTo(pos.getX(), pos.getY(), pos.getZ(), speedModifier);
- return Butterfly.this.navigation.getPath() != null && Butterfly.this.navigation.getPath().canReach();
- }
- private void setPollinatingPos(BlockPos pos) {
- Vec3 vec3 = Vec3.atBottomCenterOf(pos).add(0.0D, 0.6F, 0.0D);
- if (vec3.distanceTo(this.position()) > 1.0D) {
- this.hoverPos = vec3;
- this.setWantedPos();
- } else {
- if (this.hoverPos == null) {
- this.hoverPos = vec3;
- }
- boolean flag = this.position().distanceTo(this.hoverPos) <= 0.1D;
- boolean flag1 = true;
- if (flag) {
- boolean flag2 = this.random.nextInt(25) == 0;
- if (flag2) {
- float offset = (this.random.nextFloat() * 2.0F - 1.0F) * 0.33333334F;
- this.hoverPos = new Vec3(vec3.x() + (double) offset, vec3.y(), vec3.z() + (double) offset);
- this.navigation.stop();
- } else {
- flag1 = false;
- }
- this.getLookControl().setLookAt(vec3.x(), vec3.y(), vec3.z());
- }
- if (flag1) {
- this.setWantedPos();
- }
- }
- }
- private void setWantedPos() {
- if (this.hoverPos != null) {
- this.getMoveControl().setWantedPosition(this.hoverPos.x(), this.hoverPos.y(), this.hoverPos.z(), 0.35F);
- }
- }
- private boolean isCloserThan(BlockPos pos, int distance) {
- return pos.closerThan(this.blockPosition(), distance);
- }
- private boolean isTooFarAway(BlockPos pos) {
- return !this.isCloserThan(pos, 32);
- }
- private boolean hasReachedTarget(BlockPos pos) {
- if (this.isCloserThan(pos, 2)) {
- return true;
- } else {
- Path path = this.navigation.getPath();
- return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Sets the butterflies home/nest position by scoping out an available one nearby.
- */
- class LocateNestGoal extends Goal {
- public boolean canUse() {
- return Butterfly.this.remainingTicksBeforeLocatingNewNest == 0 && Butterfly.this.nestPos == null && Butterfly.this.wantsToEnterNest();
- }
- public boolean canContinueToUse() {
- return false;
- }
- public void start() {
- Butterfly.this.remainingTicksBeforeLocatingNewNest = 200;
- List<BlockPos> list = this.findNearbyNestsWithSpace();
- if (!list.isEmpty()) {
- for (BlockPos pos : list) {
- Butterfly.this.nestPos = pos;
- return;
- }
- Butterfly.this.nestPos = list.get(0);
- }
- }
- private List<BlockPos> findNearbyNestsWithSpace() {
- BlockPos pos = Butterfly.this.blockPosition();
- PoiManager poiManager = ((ServerLevel)Butterfly.this.level()).getPoiManager();
- Stream<PoiRecord> stream = poiManager.getInRange((holder) -> holder.is(MysticPoiTypes.BUTTERFLY_NEST.getId()), pos, 20, PoiManager.Occupancy.ANY);
- return stream.map(PoiRecord::getPos).filter(Butterfly.this::doesNestHaveSpace).sorted(Comparator.comparingDouble((pos1) -> pos1.distSqr(pos))).collect(Collectors.toList());
- }
- }
- class GoToNestGoal extends Goal {
- @Nullable
- private Path lastPath;
- GoToNestGoal() {
- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
- }
- public boolean canUse() {
- 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());
- }
- public boolean canContinueToUse() {
- return this.canUse();
- }
- public void stop() {
- Butterfly.this.navigation.stop();
- Butterfly.this.navigation.resetMaxVisitedNodesMultiplier();
- }
- public void tick() {
- if (Butterfly.this.nestPos != null) {
- if (!Butterfly.this.navigation.isInProgress()) {
- if (!Butterfly.this.isCloserThan(Butterfly.this.nestPos, 16)) {
- Butterfly.this.pathfindRandomlyTowards(Butterfly.this.nestPos);
- } else {
- boolean flag = Butterfly.this.pathfindDirectlyTowards(Butterfly.this.nestPos, 1.0D);
- if (flag) {
- if (this.lastPath != null && Butterfly.this.navigation.getPath() != null && !Butterfly.this.navigation.getPath().sameAs(this.lastPath)) {
- this.lastPath = Butterfly.this.navigation.getPath();
- }
- }
- }
- }
- }
- }
- }
- class EnterNestGoal extends Goal {
- public boolean canUse() {
- if (Butterfly.this.nestPos != null && Butterfly.this.wantsToEnterNest() && Butterfly.this.nestPos.closerToCenterThan(Butterfly.this.position(), 2.0D)) {
- BlockEntity blockEntity = Butterfly.this.level().getBlockEntity(Butterfly.this.nestPos);
- if (blockEntity instanceof ButterflyNestBlockEntity entity) {
- return !entity.isFull();
- }
- }
- return false;
- }
- public boolean canContinueToUse() {
- return false;
- }
- public void start() {
- if (Butterfly.this.level().isNight()) {
- Butterfly.this.setIsSleeping(true);
- }
- Butterfly.this.isInNest = true;
- if (Butterfly.this.nestPos != null) {
- BlockEntity blockEntity = Butterfly.this.level().getBlockEntity(Butterfly.this.nestPos);
- if (blockEntity instanceof ButterflyNestBlockEntity entity) {
- entity.addOccupant(Butterfly.this, Butterfly.this.hasNectar(), Butterfly.this.isBreeding());
- }
- }
- }
- public void stop() {
- Butterfly.this.isInNest = false;
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Either pollinates a random flower or locates and pollinates the same flower a player gave them.
- */
- class PollinateGoal extends Goal {
- private int pollinatingTicks;
- private boolean pollinating;
- private boolean willSpreadFlowersAfter;
- @Nullable
- private BlockPos flowerPos;
- private final Predicate<BlockState> VALID_POLLINATION_BLOCKS = (state) -> {
- if (state.is(BlockTags.FLOWERS)) {
- if (state.is(Blocks.SUNFLOWER)) {
- return state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER;
- } else {
- return true;
- }
- } else {
- return false;
- }
- };
- PollinateGoal() {
- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
- }
- public boolean canUse() {
- if (!Butterfly.this.hasNest()) {
- return false;
- } else if (Butterfly.this.hasNectar()) {
- return false;
- } else if (Butterfly.this.level().isRaining()) {
- return false;
- } else {
- if (Butterfly.this.canPollinate()) {
- return Butterfly.this.wasGivenFlower() || Butterfly.this.wantsToPollinate();
- } else {
- return false;
- }
- }
- }
- public boolean canContinueToUse() {
- if (Butterfly.this.level().isRaining()) {
- return false;
- } else {
- return this.flowerPos != null && !(this.pollinatingTicks > 600);
- }
- }
- public void start() {
- this.pollinatingTicks = 0;
- Optional<BlockPos> optional = this.findNearbyFlower();
- if (optional.isPresent()) {
- this.flowerPos = optional.get();
- }
- }
- public void stop() {
- this.pollinating = false;
- if (this.pollinatingTicks > 600) {
- Butterfly.this.nectarPoints += 3;
- }
- if (!Butterfly.this.wasGivenFlower()) {
- this.flowerPos = null;
- Butterfly.this.navigation.stop();
- Butterfly.this.ticksSincePollinated = 0; // 2 minute cooldown
- } else {
- this.willSpreadFlowersAfter = true;
- }
- }
- public boolean requiresUpdateEveryTick() {
- return true;
- }
- public void tick() {
- Optional<BlockPos> optional = this.findNearbyFlower();
- if (optional.isEmpty()) {
- this.flowerPos = null;
- Butterfly.this.ticksSincePollinated = 2200;
- }
- if (this.flowerPos != null) {
- if (!Butterfly.this.hasReachedTarget(this.flowerPos)) {
- Butterfly.this.pathfindDirectlyTowards(this.flowerPos, 1.0D);
- } else {
- this.pollinating = true;
- ++this.pollinatingTicks;
- Butterfly.this.setPollinatingPos(this.flowerPos);
- }
- }
- }
- private Optional<BlockPos> findNearbyFlower() {
- return Butterfly.this.givenFlower != null ? this.findNearestBlock((block) -> block.is(Butterfly.this.givenFlower), 16.0D) : this.findNearestBlock(this.VALID_POLLINATION_BLOCKS, 12.0D);
- }
- private Optional<BlockPos> findNearestBlock(Predicate<BlockState> predicate, double distance) {
- BlockPos pos = Butterfly.this.blockPosition();
- BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
- for (int i = 0; (double)i <= distance; i = i > 0 ? -i : 1 - i) {
- for (int j = 0; (double)j < distance; ++j) {
- for (int k = 0; k <= j; k = k > 0 ? -k : 1 - k) {
- for (int l = k < j && k > -j ? j : 0; l <= j; l = l > 0 ? -l : 1 - l) {
- mutablePos.setWithOffset(pos, k, i - 1, l);
- if (pos.closerThan(mutablePos, distance) && predicate.test(Butterfly.this.level().getBlockState(mutablePos))) {
- return Optional.of(mutablePos);
- }
- }
- }
- }
- }
- return Optional.empty();
- }
- public boolean isPollinating() {
- return this.pollinating;
- }
- public void stopPollinating() {
- this.pollinating = false;
- this.flowerPos = null;
- Butterfly.this.givenFlower = null;
- Butterfly.this.ticksSincePollinated = 0;
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- /**
- * The main feature of butterflies; they plant 2 flowers for every 1 flower they collect nectar from.
- */
- class SpreadFlowersGoal extends Goal {
- private int spreadFlowerTicks;
- private boolean plantingFlower;
- @Nullable
- private BlockPos emptyPos;
- SpreadFlowersGoal() {
- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
- }
- public boolean canUse() {
- return Butterfly.this.pollinateGoal.willSpreadFlowersAfter;
- }
- public boolean canContinueToUse() {
- return Butterfly.this.hasNectar() && Butterfly.this.wasGivenFlower() && this.spreadFlowerTicks < 1200; // 1 minute
- }
- public void start() {
- this.spreadFlowerTicks = 0;
- this.emptyPos = this.findRandomEmptyPos();
- }
- public void stop() {
- this.plantingFlower = false;
- if (!Butterfly.this.level().isClientSide) {
- if (this.emptyPos != null) {
- if (Butterfly.this.givenFlower != null) {
- BlockState flowerState = Butterfly.this.givenFlower.defaultBlockState();
- if (Butterfly.this.level().isEmptyBlock(this.emptyPos) && flowerState.canSurvive(Butterfly.this.level(), this.emptyPos)) {
- Butterfly.this.level().setBlockAndUpdate(this.emptyPos, flowerState);
- Butterfly.this.level().gameEvent(GameEvent.BLOCK_PLACE, this.emptyPos, GameEvent.Context.of(Butterfly.this, flowerState));
- }
- Butterfly.this.nectarPoints -= 1;
- this.emptyPos = null;
- }
- }
- }
- if (Butterfly.this.nectarPoints == 0) {
- Butterfly.this.givenFlower = null;
- Butterfly.this.pollinateGoal.flowerPos = null;
- Butterfly.this.pollinateGoal.willSpreadFlowersAfter = false;
- Butterfly.this.navigation.stop();
- Butterfly.this.ticksSincePollinated = 0; // 2 minute cooldown
- }
- }
- public void tick() {
- if (this.emptyPos != null) {
- if (!Butterfly.this.hasReachedTarget(this.emptyPos)) {
- Butterfly.this.pathfindDirectlyTowards(this.emptyPos, 0.4D);
- } else {
- this.plantingFlower = true;
- ++this.spreadFlowerTicks;
- Butterfly.this.setPollinatingPos(this.emptyPos);
- }
- } else {
- this.emptyPos = this.findRandomEmptyPos();
- }
- }
- /**
- * @return a random empty space near the original flower pos a butterfly can plant a flower at.
- */
- private BlockPos findRandomEmptyPos() {
- Map<Integer, BlockPos> map = Maps.newHashMap();
- BlockPos flowerPos = Butterfly.this.pollinateGoal.flowerPos;
- BlockPos currentPos = flowerPos != null ? flowerPos : Butterfly.this.blockPosition();
- for (int i = 0; i <= 14; i++) {
- BlockPos pos;
- int x = Mth.floor(currentPos.getX() + (random.nextBoolean() ? random.nextInt(3) : -random.nextInt(3)));
- int y = Mth.floor(currentPos.getY());
- int z = Mth.floor(currentPos.getZ() + (random.nextBoolean() ? random.nextInt(3) : -random.nextInt(3)));
- pos = new BlockPos(x, y, z);
- if (Butterfly.this.level().getBlockState(pos).isAir() && Butterfly.this.level().getBlockState(pos.below()).is(Blocks.GRASS_BLOCK)) {
- map.put(i, pos);
- }
- }
- return map.get(0);
- }
- public boolean isPlantingFlower() {
- return this.plantingFlower;
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- public class BreedGoal extends Goal {
- private int loveTime;
- @Nullable
- protected Butterfly partner;
- BreedGoal() {
- this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
- }
- public boolean canUse() {
- if (!Butterfly.this.isInLove()) {
- return false;
- } else {
- Butterfly.this.breeding = true;
- this.partner = this.getNearbyPartner();
- return this.partner != null;
- }
- }
- public boolean canContinueToUse() {
- return Butterfly.this.breeding && this.partner != null && this.partner.isAlive() && this.partner.isInLove() && this.loveTime < 400;
- }
- public void stop() {
- this.partner = null;
- this.loveTime = 0;
- }
- public void tick() {
- if (this.partner != null) {
- if (Butterfly.this.nestPos != null) {
- if (!Butterfly.this.hasReachedTarget(Butterfly.this.nestPos)) {
- Butterfly.this.pathfindDirectlyTowards(Butterfly.this.nestPos, 1.0D);
- } else {
- if (Butterfly.this.isInNest()) {
- ++this.loveTime;
- if (this.loveTime >= this.adjustedTickDelay(400)) {
- Butterfly.this.setBreeding(false);
- }
- }
- }
- }
- }
- }
- @Nullable
- private Butterfly getNearbyPartner() {
- List<Butterfly> list = Butterfly.this.level().getNearbyEntities(Butterfly.class, TargetingConditions.forNonCombat().range(8.0D).ignoreLineOfSight(), Butterfly.this, Butterfly.this.getBoundingBox().inflate(8.0D));
- double distance = Double.MAX_VALUE;
- Butterfly partner = null;
- for (Butterfly butterflies : list) {
- if (Butterfly.this.canMate(butterflies) && Butterfly.this.distanceToSqr(butterflies) < distance) {
- partner = butterflies;
- distance = Butterfly.this.distanceToSqr(butterflies);
- }
- }
- return partner;
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- class WanderGoal extends Goal {
- WanderGoal() {
- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
- }
- public boolean canUse() {
- return Butterfly.this.navigation.isDone() && Butterfly.this.random.nextInt(10) == 0;
- }
- public boolean canContinueToUse() {
- return Butterfly.this.navigation.isInProgress();
- }
- public void start() {
- Vec3 vec3 = this.findPos();
- if (vec3 != null) {
- Butterfly.this.navigation.moveTo(Butterfly.this.navigation.createPath(BlockPos.containing(vec3), 1), 1.0D);
- }
- }
- @Nullable
- private Vec3 findPos() {
- Vec3 vec3;
- if (Butterfly.this.isNestValid() && Butterfly.this.nestPos != null && !Butterfly.this.isCloserThan(Butterfly.this.nestPos, 22)) {
- Vec3 vec31 = Vec3.atCenterOf(Butterfly.this.nestPos);
- vec3 = vec31.subtract(Butterfly.this.position()).normalize();
- } else {
- vec3 = Butterfly.this.getViewVector(0.0F);
- }
- Vec3 vec32 = HoverRandomPos.getPos(Butterfly.this, 8, 7, vec3.x, vec3.z, ((float)Math.PI / 2F), 3, 1);
- return vec32 != null ? vec32 : AirAndWaterRandomPos.getPos(Butterfly.this, 8, 4, -2, vec3.x, vec3.z, ((float)Math.PI / 2F));
- }
- }
- class ButterflyLookControl extends LookControl {
- ButterflyLookControl(Mob mob) {
- super(mob);
- }
- public void tick() {
- super.tick();
- }
- protected boolean resetXRotOnTick() {
- return !Butterfly.this.pollinateGoal.isPollinating() || !Butterfly.this.spreadFlowersGoal.isPlantingFlower();
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- public enum Type implements StringRepresentable {
- APRICOT(0, "apricot"),
- JELLY(1, "jelly");
- private final int id;
- private final String name;
- Type(int id, String name) {
- this.id = id;
- this.name = name;
- }
- public int getId() {
- return this.id;
- }
- public String getSerializedName() {
- return this.name;
- }
- public static Type byId(int id) {
- return ByIdMap.continuous(Type::getId, values(), ByIdMap.OutOfBoundsStrategy.ZERO).apply(id);
- }
- public static Type byName(String name) {
- return StringRepresentable.fromEnum(Type::values).byName(name, APRICOT);
- }
- }
- }
Add Comment
Please, Sign In to add comment