2025-11-24 22:52:51 +03:00

1361 lines
51 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.Lists
* it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.entity.animal;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
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.sounds.SoundEvents;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.PoiTypeTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.Mth;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.VisibleForDebug;
import net.minecraft.util.debug.DebugBeeInfo;
import net.minecraft.util.debug.DebugSubscriptions;
import net.minecraft.util.debug.DebugValueSource;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.Difficulty;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.attribute.EnvironmentAttributes;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.NeutralMob;
import net.minecraft.world.entity.PathfinderMob;
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.BreedGoal;
import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.entity.ai.goal.FollowParentGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.GoalSelector;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.ai.goal.TemptGoal;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal;
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
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.flag.FeatureElement;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.CropBlock;
import net.minecraft.world.level.block.DoublePlantBlock;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.StemBlock;
import net.minecraft.world.level.block.SweetBerryBushBlock;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.level.pathfinder.PathType;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
public class Bee
extends Animal
implements FlyingAnimal,
NeutralMob {
public static final float FLAP_DEGREES_PER_TICK = 120.32113f;
public static final int TICKS_PER_FLAP = Mth.ceil(1.4959966f);
private static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(Bee.class, EntityDataSerializers.BYTE);
private static final EntityDataAccessor<Long> DATA_ANGER_END_TIME = SynchedEntityData.defineId(Bee.class, EntityDataSerializers.LONG);
private static final int FLAG_ROLL = 2;
private static final int FLAG_HAS_STUNG = 4;
private static final int FLAG_HAS_NECTAR = 8;
private static final int STING_DEATH_COUNTDOWN = 1200;
private static final int TICKS_BEFORE_GOING_TO_KNOWN_FLOWER = 600;
private static final int TICKS_WITHOUT_NECTAR_BEFORE_GOING_HOME = 3600;
private static final int MIN_ATTACK_DIST = 4;
private static final int MAX_CROPS_GROWABLE = 10;
private static final int POISON_SECONDS_NORMAL = 10;
private static final int POISON_SECONDS_HARD = 18;
private static final int TOO_FAR_DISTANCE = 48;
private static final int HIVE_CLOSE_ENOUGH_DISTANCE = 2;
private static final int RESTRICTED_WANDER_DISTANCE_REDUCTION = 24;
private static final int DEFAULT_WANDER_DISTANCE_REDUCTION = 16;
private static final int PATHFIND_TO_HIVE_WHEN_CLOSER_THAN = 16;
private static final int HIVE_SEARCH_DISTANCE = 20;
public static final String TAG_CROPS_GROWN_SINCE_POLLINATION = "CropsGrownSincePollination";
public static final String TAG_CANNOT_ENTER_HIVE_TICKS = "CannotEnterHiveTicks";
public static final String TAG_TICKS_SINCE_POLLINATION = "TicksSincePollination";
public static final String TAG_HAS_STUNG = "HasStung";
public static final String TAG_HAS_NECTAR = "HasNectar";
public static final String TAG_FLOWER_POS = "flower_pos";
public static final String TAG_HIVE_POS = "hive_pos";
public static final boolean DEFAULT_HAS_NECTAR = false;
private static final boolean DEFAULT_HAS_STUNG = false;
private static final int DEFAULT_TICKS_SINCE_POLLINATION = 0;
private static final int DEFAULT_CANNOT_ENTER_HIVE_TICKS = 0;
private static final int DEFAULT_CROPS_GROWN_SINCE_POLLINATION = 0;
private static final UniformInt PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39);
private @Nullable EntityReference<LivingEntity> persistentAngerTarget;
private float rollAmount;
private float rollAmountO;
private int timeSinceSting;
private int ticksWithoutNectarSinceExitingHive = 0;
private int stayOutOfHiveCountdown = 0;
private int numCropsGrownSincePollination = 0;
private static final int COOLDOWN_BEFORE_LOCATING_NEW_HIVE = 200;
private int remainingCooldownBeforeLocatingNewHive;
private static final int COOLDOWN_BEFORE_LOCATING_NEW_FLOWER = 200;
private static final int MIN_FIND_FLOWER_RETRY_COOLDOWN = 20;
private static final int MAX_FIND_FLOWER_RETRY_COOLDOWN = 60;
private int remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
private @Nullable BlockPos savedFlowerPos;
private @Nullable BlockPos hivePos;
private BeePollinateGoal beePollinateGoal;
private BeeGoToHiveGoal goToHiveGoal;
private BeeGoToKnownFlowerGoal goToKnownFlowerGoal;
private int underWaterTicks;
public Bee(EntityType<? extends Bee> type, Level level) {
super((EntityType<? extends Animal>)type, level);
this.moveControl = new FlyingMoveControl(this, 20, true);
this.lookControl = new BeeLookControl(this);
this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0f);
this.setPathfindingMalus(PathType.WATER, -1.0f);
this.setPathfindingMalus(PathType.WATER_BORDER, 16.0f);
this.setPathfindingMalus(PathType.COCOA, -1.0f);
this.setPathfindingMalus(PathType.FENCE, -1.0f);
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
entityData.define(DATA_FLAGS_ID, (byte)0);
entityData.define(DATA_ANGER_END_TIME, -1L);
}
@Override
public float getWalkTargetValue(BlockPos pos, LevelReader level) {
if (level.getBlockState(pos).isAir()) {
return 10.0f;
}
return 0.0f;
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new BeeAttackGoal(this, 1.4f, true));
this.goalSelector.addGoal(1, new BeeEnterHiveGoal());
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, i -> i.is(ItemTags.BEE_FOOD), false));
this.goalSelector.addGoal(3, new ValidateHiveGoal());
this.goalSelector.addGoal(3, new ValidateFlowerGoal());
this.beePollinateGoal = new BeePollinateGoal();
this.goalSelector.addGoal(4, this.beePollinateGoal);
this.goalSelector.addGoal(5, new FollowParentGoal(this, 1.25));
this.goalSelector.addGoal(5, new BeeLocateHiveGoal());
this.goToHiveGoal = new BeeGoToHiveGoal();
this.goalSelector.addGoal(5, this.goToHiveGoal);
this.goToKnownFlowerGoal = new BeeGoToKnownFlowerGoal();
this.goalSelector.addGoal(6, this.goToKnownFlowerGoal);
this.goalSelector.addGoal(7, new BeeGrowCropGoal());
this.goalSelector.addGoal(8, new BeeWanderGoal());
this.goalSelector.addGoal(9, new FloatGoal(this));
this.targetSelector.addGoal(1, new BeeHurtByOtherGoal(this).setAlertOthers(new Class[0]));
this.targetSelector.addGoal(2, new BeeBecomeAngryTargetGoal(this));
this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<Bee>(this, true));
}
@Override
protected void addAdditionalSaveData(ValueOutput output) {
super.addAdditionalSaveData(output);
output.storeNullable(TAG_HIVE_POS, BlockPos.CODEC, this.hivePos);
output.storeNullable(TAG_FLOWER_POS, BlockPos.CODEC, this.savedFlowerPos);
output.putBoolean(TAG_HAS_NECTAR, this.hasNectar());
output.putBoolean(TAG_HAS_STUNG, this.hasStung());
output.putInt(TAG_TICKS_SINCE_POLLINATION, this.ticksWithoutNectarSinceExitingHive);
output.putInt(TAG_CANNOT_ENTER_HIVE_TICKS, this.stayOutOfHiveCountdown);
output.putInt(TAG_CROPS_GROWN_SINCE_POLLINATION, this.numCropsGrownSincePollination);
this.addPersistentAngerSaveData(output);
}
@Override
protected void readAdditionalSaveData(ValueInput input) {
super.readAdditionalSaveData(input);
this.setHasNectar(input.getBooleanOr(TAG_HAS_NECTAR, false));
this.setHasStung(input.getBooleanOr(TAG_HAS_STUNG, false));
this.ticksWithoutNectarSinceExitingHive = input.getIntOr(TAG_TICKS_SINCE_POLLINATION, 0);
this.stayOutOfHiveCountdown = input.getIntOr(TAG_CANNOT_ENTER_HIVE_TICKS, 0);
this.numCropsGrownSincePollination = input.getIntOr(TAG_CROPS_GROWN_SINCE_POLLINATION, 0);
this.hivePos = input.read(TAG_HIVE_POS, BlockPos.CODEC).orElse(null);
this.savedFlowerPos = input.read(TAG_FLOWER_POS, BlockPos.CODEC).orElse(null);
this.readPersistentAngerSaveData(this.level(), input);
}
@Override
public boolean doHurtTarget(ServerLevel level, Entity target) {
DamageSource damageSource = this.damageSources().sting(this);
boolean wasHurt = target.hurtServer(level, damageSource, (int)this.getAttributeValue(Attributes.ATTACK_DAMAGE));
if (wasHurt) {
EnchantmentHelper.doPostAttackEffects(level, target, damageSource);
if (target instanceof LivingEntity) {
LivingEntity livingTarget = (LivingEntity)target;
livingTarget.setStingerCount(livingTarget.getStingerCount() + 1);
int poisonTime = 0;
if (this.level().getDifficulty() == Difficulty.NORMAL) {
poisonTime = 10;
} else if (this.level().getDifficulty() == Difficulty.HARD) {
poisonTime = 18;
}
if (poisonTime > 0) {
livingTarget.addEffect(new MobEffectInstance(MobEffects.POISON, poisonTime * 20, 0), this);
}
}
this.setHasStung(true);
this.stopBeingAngry();
this.playSound(SoundEvents.BEE_STING, 1.0f, 1.0f);
}
return wasHurt;
}
@Override
public void tick() {
super.tick();
if (this.hasNectar() && this.getCropsGrownSincePollination() < 10 && this.random.nextFloat() < 0.05f) {
for (int i = 0; i < this.random.nextInt(2) + 1; ++i) {
this.spawnFluidParticle(this.level(), this.getX() - (double)0.3f, this.getX() + (double)0.3f, this.getZ() - (double)0.3f, this.getZ() + (double)0.3f, this.getY(0.5), ParticleTypes.FALLING_NECTAR);
}
}
this.updateRollAmount();
}
private void spawnFluidParticle(Level level, double x1, double x2, double z1, double z2, double y, ParticleOptions dripParticle) {
level.addParticle(dripParticle, Mth.lerp(level.random.nextDouble(), x1, x2), y, Mth.lerp(level.random.nextDouble(), z1, z2), 0.0, 0.0, 0.0);
}
private void pathfindRandomlyTowards(BlockPos targetPos) {
Vec3 nextPosTowards;
Vec3 targetVec = Vec3.atBottomCenterOf(targetPos);
int yAdjust = 0;
BlockPos beePos = this.blockPosition();
int yDelta = (int)targetVec.y - beePos.getY();
if (yDelta > 2) {
yAdjust = 4;
} else if (yDelta < -2) {
yAdjust = -4;
}
int xzDist = 6;
int yDist = 8;
int dist = beePos.distManhattan(targetPos);
if (dist < 15) {
xzDist = dist / 2;
yDist = dist / 2;
}
if ((nextPosTowards = AirRandomPos.getPosTowards(this, xzDist, yDist, yAdjust, targetVec, 0.3141592741012573)) == null) {
return;
}
this.navigation.setMaxVisitedNodesMultiplier(0.5f);
this.navigation.moveTo(nextPosTowards.x, nextPosTowards.y, nextPosTowards.z, 1.0);
}
public @Nullable BlockPos getSavedFlowerPos() {
return this.savedFlowerPos;
}
public boolean hasSavedFlowerPos() {
return this.savedFlowerPos != null;
}
public void setSavedFlowerPos(BlockPos savedFlowerPos) {
this.savedFlowerPos = savedFlowerPos;
}
@VisibleForDebug
public int getTravellingTicks() {
return Math.max(this.goToHiveGoal.travellingTicks, this.goToKnownFlowerGoal.travellingTicks);
}
@VisibleForDebug
public List<BlockPos> getBlacklistedHives() {
return this.goToHiveGoal.blacklistedTargets;
}
private boolean isTiredOfLookingForNectar() {
return this.ticksWithoutNectarSinceExitingHive > 3600;
}
private void dropHive() {
this.hivePos = null;
this.remainingCooldownBeforeLocatingNewHive = 200;
}
private void dropFlower() {
this.savedFlowerPos = null;
this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
}
private boolean wantsToEnterHive() {
if (this.stayOutOfHiveCountdown > 0 || this.beePollinateGoal.isPollinating() || this.hasStung() || this.getTarget() != null) {
return false;
}
boolean wantsToEnterHive = this.hasNectar() || this.isTiredOfLookingForNectar() || this.level().environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, this.position()) != false;
return wantsToEnterHive && !this.isHiveNearFire();
}
public void setStayOutOfHiveCountdown(int ticks) {
this.stayOutOfHiveCountdown = ticks;
}
public float getRollAmount(float a) {
return Mth.lerp(a, this.rollAmountO, this.rollAmount);
}
private void updateRollAmount() {
this.rollAmountO = this.rollAmount;
this.rollAmount = this.isRolling() ? Math.min(1.0f, this.rollAmount + 0.2f) : Math.max(0.0f, this.rollAmount - 0.24f);
}
@Override
protected void customServerAiStep(ServerLevel level) {
boolean hasStung = this.hasStung();
this.underWaterTicks = this.isInWater() ? ++this.underWaterTicks : 0;
if (this.underWaterTicks > 20) {
this.hurtServer(level, this.damageSources().drown(), 1.0f);
}
if (hasStung) {
++this.timeSinceSting;
if (this.timeSinceSting % 5 == 0 && this.random.nextInt(Mth.clamp(1200 - this.timeSinceSting, 1, 1200)) == 0) {
this.hurtServer(level, this.damageSources().generic(), this.getHealth());
}
}
if (!this.hasNectar()) {
++this.ticksWithoutNectarSinceExitingHive;
}
this.updatePersistentAnger(level, false);
}
public void resetTicksWithoutNectarSinceExitingHive() {
this.ticksWithoutNectarSinceExitingHive = 0;
}
private boolean isHiveNearFire() {
BeehiveBlockEntity beehiveBlockEntity = this.getBeehiveBlockEntity();
return beehiveBlockEntity != null && beehiveBlockEntity.isFireNearby();
}
@Override
public long getPersistentAngerEndTime() {
return this.entityData.get(DATA_ANGER_END_TIME);
}
@Override
public void setPersistentAngerEndTime(long endTime) {
this.entityData.set(DATA_ANGER_END_TIME, endTime);
}
@Override
public @Nullable EntityReference<LivingEntity> getPersistentAngerTarget() {
return this.persistentAngerTarget;
}
@Override
public void setPersistentAngerTarget(@Nullable EntityReference<LivingEntity> persistentAngerTarget) {
this.persistentAngerTarget = persistentAngerTarget;
}
@Override
public void startPersistentAngerTimer() {
this.setTimeToRemainAngry(PERSISTENT_ANGER_TIME.sample(this.random));
}
private boolean doesHiveHaveSpace(BlockPos hivePos) {
BlockEntity blockEntity = this.level().getBlockEntity(hivePos);
if (blockEntity instanceof BeehiveBlockEntity) {
return !((BeehiveBlockEntity)blockEntity).isFull();
}
return false;
}
@VisibleForDebug
public boolean hasHive() {
return this.hivePos != null;
}
@VisibleForDebug
public @Nullable BlockPos getHivePos() {
return this.hivePos;
}
@VisibleForDebug
public GoalSelector getGoalSelector() {
return this.goalSelector;
}
private int getCropsGrownSincePollination() {
return this.numCropsGrownSincePollination;
}
private void resetNumCropsGrownSincePollination() {
this.numCropsGrownSincePollination = 0;
}
private void incrementNumCropsGrownSincePollination() {
++this.numCropsGrownSincePollination;
}
@Override
public void aiStep() {
super.aiStep();
if (!this.level().isClientSide()) {
if (this.stayOutOfHiveCountdown > 0) {
--this.stayOutOfHiveCountdown;
}
if (this.remainingCooldownBeforeLocatingNewHive > 0) {
--this.remainingCooldownBeforeLocatingNewHive;
}
if (this.remainingCooldownBeforeLocatingNewFlower > 0) {
--this.remainingCooldownBeforeLocatingNewFlower;
}
boolean shouldRoll = this.isAngry() && !this.hasStung() && this.getTarget() != null && this.getTarget().distanceToSqr(this) < 4.0;
this.setRolling(shouldRoll);
if (this.tickCount % 20 == 0 && !this.isHiveValid()) {
this.hivePos = null;
}
}
}
private @Nullable BeehiveBlockEntity getBeehiveBlockEntity() {
if (this.hivePos == null) {
return null;
}
if (this.isTooFarAway(this.hivePos)) {
return null;
}
return this.level().getBlockEntity(this.hivePos, BlockEntityType.BEEHIVE).orElse(null);
}
private boolean isHiveValid() {
return this.getBeehiveBlockEntity() != null;
}
public boolean hasNectar() {
return this.getFlag(8);
}
private void setHasNectar(boolean hasNectar) {
if (hasNectar) {
this.resetTicksWithoutNectarSinceExitingHive();
}
this.setFlag(8, hasNectar);
}
public boolean hasStung() {
return this.getFlag(4);
}
private void setHasStung(boolean hasStung) {
this.setFlag(4, hasStung);
}
private boolean isRolling() {
return this.getFlag(2);
}
private void setRolling(boolean rolling) {
this.setFlag(2, rolling);
}
private boolean isTooFarAway(BlockPos targetPos) {
return !this.closerThan(targetPos, 48);
}
private void setFlag(int flag, boolean value) {
if (value) {
this.entityData.set(DATA_FLAGS_ID, (byte)(this.entityData.get(DATA_FLAGS_ID) | flag));
} else {
this.entityData.set(DATA_FLAGS_ID, (byte)(this.entityData.get(DATA_FLAGS_ID) & ~flag));
}
}
private boolean getFlag(int flag) {
return (this.entityData.get(DATA_FLAGS_ID) & flag) != 0;
}
public static AttributeSupplier.Builder createAttributes() {
return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.FLYING_SPEED, 0.6f).add(Attributes.MOVEMENT_SPEED, 0.3f).add(Attributes.ATTACK_DAMAGE, 2.0);
}
@Override
protected PathNavigation createNavigation(Level level) {
FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level){
@Override
public boolean isStableDestination(BlockPos pos) {
return !this.level.getBlockState(pos.below()).isAir();
}
@Override
public void tick() {
if (Bee.this.beePollinateGoal.isPollinating()) {
return;
}
super.tick();
}
};
flyingPathNavigation.setCanOpenDoors(false);
flyingPathNavigation.setCanFloat(false);
flyingPathNavigation.setRequiredPathLength(48.0f);
return flyingPathNavigation;
}
@Override
public InteractionResult mobInteract(Player player, InteractionHand hand) {
FlowerBlock flower;
MobEffectInstance effect;
BlockItem blockItem;
FeatureElement featureElement;
ItemStack heldItem = player.getItemInHand(hand);
if (this.isFood(heldItem) && (featureElement = heldItem.getItem()) instanceof BlockItem && (featureElement = (blockItem = (BlockItem)featureElement).getBlock()) instanceof FlowerBlock && (effect = (flower = (FlowerBlock)featureElement).getBeeInteractionEffect()) != null) {
this.usePlayerItem(player, hand, heldItem);
if (!this.level().isClientSide()) {
this.addEffect(effect);
}
return InteractionResult.SUCCESS;
}
return super.mobInteract(player, hand);
}
@Override
public boolean isFood(ItemStack itemStack) {
return itemStack.is(ItemTags.BEE_FOOD);
}
@Override
protected void playStepSound(BlockPos pos, BlockState blockState) {
}
@Override
protected SoundEvent getAmbientSound() {
return null;
}
@Override
protected SoundEvent getHurtSound(DamageSource source) {
return SoundEvents.BEE_HURT;
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.BEE_DEATH;
}
@Override
protected float getSoundVolume() {
return 0.4f;
}
@Override
public @Nullable Bee getBreedOffspring(ServerLevel level, AgeableMob partner) {
return EntityType.BEE.create(level, EntitySpawnReason.BREEDING);
}
@Override
protected void checkFallDamage(double ya, boolean onGround, BlockState onState, BlockPos pos) {
}
@Override
public boolean isFlapping() {
return this.isFlying() && this.tickCount % TICKS_PER_FLAP == 0;
}
@Override
public boolean isFlying() {
return !this.onGround();
}
public void dropOffNectar() {
this.setHasNectar(false);
this.resetNumCropsGrownSincePollination();
}
@Override
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
if (this.isInvulnerableTo(level, source)) {
return false;
}
this.beePollinateGoal.stopPollinating();
return super.hurtServer(level, source, damage);
}
@Override
protected void jumpInLiquid(TagKey<Fluid> type) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0, 0.01, 0.0));
}
@Override
public Vec3 getLeashOffset() {
return new Vec3(0.0, 0.5f * this.getEyeHeight(), this.getBbWidth() * 0.2f);
}
private boolean closerThan(BlockPos targetPos, int distance) {
return targetPos.closerThan(this.blockPosition(), distance);
}
public void setHivePos(BlockPos hivePos) {
this.hivePos = hivePos;
}
public static boolean attractsBees(BlockState state) {
if (state.is(BlockTags.BEE_ATTRACTIVE)) {
if (state.getValueOrElse(BlockStateProperties.WATERLOGGED, false).booleanValue()) {
return false;
}
if (state.is(Blocks.SUNFLOWER)) {
return state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER;
}
return true;
}
return false;
}
@Override
public void registerDebugValues(ServerLevel level, DebugValueSource.Registration registration) {
super.registerDebugValues(level, registration);
registration.register(DebugSubscriptions.BEES, () -> new DebugBeeInfo(Optional.ofNullable(this.getHivePos()), Optional.ofNullable(this.getSavedFlowerPos()), this.getTravellingTicks(), this.getBlacklistedHives()));
}
private class BeeLookControl
extends LookControl {
BeeLookControl(Mob mob) {
super(mob);
}
@Override
public void tick() {
if (Bee.this.isAngry()) {
return;
}
super.tick();
}
@Override
protected boolean resetXRotOnTick() {
return !Bee.this.beePollinateGoal.isPollinating();
}
}
private class BeeAttackGoal
extends MeleeAttackGoal {
BeeAttackGoal(PathfinderMob mob, double speedModifier, boolean trackTarget) {
super(mob, speedModifier, trackTarget);
}
@Override
public boolean canUse() {
return super.canUse() && Bee.this.isAngry() && !Bee.this.hasStung();
}
@Override
public boolean canContinueToUse() {
return super.canContinueToUse() && Bee.this.isAngry() && !Bee.this.hasStung();
}
}
private class BeeEnterHiveGoal
extends BaseBeeGoal {
private BeeEnterHiveGoal() {
}
@Override
public boolean canBeeUse() {
BeehiveBlockEntity beehiveBlockEntity;
if (Bee.this.hivePos != null && Bee.this.wantsToEnterHive() && Bee.this.hivePos.closerToCenterThan(Bee.this.position(), 2.0) && (beehiveBlockEntity = Bee.this.getBeehiveBlockEntity()) != null) {
if (beehiveBlockEntity.isFull()) {
Bee.this.hivePos = null;
} else {
return true;
}
}
return false;
}
@Override
public boolean canBeeContinueToUse() {
return false;
}
@Override
public void start() {
BeehiveBlockEntity beehiveBlockEntity = Bee.this.getBeehiveBlockEntity();
if (beehiveBlockEntity != null) {
beehiveBlockEntity.addOccupant(Bee.this);
}
}
}
private class ValidateHiveGoal
extends BaseBeeGoal {
private final int VALIDATE_HIVE_COOLDOWN;
private long lastValidateTick;
private ValidateHiveGoal() {
this.VALIDATE_HIVE_COOLDOWN = Mth.nextInt(Bee.this.random, 20, 40);
this.lastValidateTick = -1L;
}
@Override
public void start() {
if (Bee.this.hivePos != null && Bee.this.level().isLoaded(Bee.this.hivePos) && !Bee.this.isHiveValid()) {
Bee.this.dropHive();
}
this.lastValidateTick = Bee.this.level().getGameTime();
}
@Override
public boolean canBeeUse() {
return Bee.this.level().getGameTime() > this.lastValidateTick + (long)this.VALIDATE_HIVE_COOLDOWN;
}
@Override
public boolean canBeeContinueToUse() {
return false;
}
}
private class ValidateFlowerGoal
extends BaseBeeGoal {
private final int validateFlowerCooldown;
private long lastValidateTick;
private ValidateFlowerGoal() {
this.validateFlowerCooldown = Mth.nextInt(Bee.this.random, 20, 40);
this.lastValidateTick = -1L;
}
@Override
public void start() {
if (Bee.this.savedFlowerPos != null && Bee.this.level().isLoaded(Bee.this.savedFlowerPos) && !this.isFlower(Bee.this.savedFlowerPos)) {
Bee.this.dropFlower();
}
this.lastValidateTick = Bee.this.level().getGameTime();
}
@Override
public boolean canBeeUse() {
return Bee.this.level().getGameTime() > this.lastValidateTick + (long)this.validateFlowerCooldown;
}
@Override
public boolean canBeeContinueToUse() {
return false;
}
private boolean isFlower(BlockPos flowerPos) {
return Bee.attractsBees(Bee.this.level().getBlockState(flowerPos));
}
}
private class BeePollinateGoal
extends BaseBeeGoal {
private static final int MIN_POLLINATION_TICKS = 400;
private static final double ARRIVAL_THRESHOLD = 0.1;
private static final int POSITION_CHANGE_CHANCE = 25;
private static final float SPEED_MODIFIER = 0.35f;
private static final float HOVER_HEIGHT_WITHIN_FLOWER = 0.6f;
private static final float HOVER_POS_OFFSET = 0.33333334f;
private static final int FLOWER_SEARCH_RADIUS = 5;
private int successfulPollinatingTicks;
private int lastSoundPlayedTick;
private boolean pollinating;
private @Nullable Vec3 hoverPos;
private int pollinatingTicks;
private static final int MAX_POLLINATING_TICKS = 600;
private Long2LongOpenHashMap unreachableFlowerCache;
BeePollinateGoal() {
this.unreachableFlowerCache = new Long2LongOpenHashMap();
this.setFlags(EnumSet.of(Goal.Flag.MOVE));
}
@Override
public boolean canBeeUse() {
if (Bee.this.remainingCooldownBeforeLocatingNewFlower > 0) {
return false;
}
if (Bee.this.hasNectar()) {
return false;
}
if (Bee.this.level().isRaining()) {
return false;
}
Optional<BlockPos> nearbyPos = this.findNearbyFlower();
if (nearbyPos.isPresent()) {
Bee.this.savedFlowerPos = nearbyPos.get();
Bee.this.navigation.moveTo((double)Bee.this.savedFlowerPos.getX() + 0.5, (double)Bee.this.savedFlowerPos.getY() + 0.5, (double)Bee.this.savedFlowerPos.getZ() + 0.5, 1.2f);
return true;
}
Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60);
return false;
}
@Override
public boolean canBeeContinueToUse() {
if (!this.pollinating) {
return false;
}
if (!Bee.this.hasSavedFlowerPos()) {
return false;
}
if (Bee.this.level().isRaining()) {
return false;
}
if (this.hasPollinatedLongEnough()) {
return Bee.this.random.nextFloat() < 0.2f;
}
return true;
}
private boolean hasPollinatedLongEnough() {
return this.successfulPollinatingTicks > 400;
}
private boolean isPollinating() {
return this.pollinating;
}
private void stopPollinating() {
this.pollinating = false;
}
@Override
public void start() {
this.successfulPollinatingTicks = 0;
this.pollinatingTicks = 0;
this.lastSoundPlayedTick = 0;
this.pollinating = true;
Bee.this.resetTicksWithoutNectarSinceExitingHive();
}
@Override
public void stop() {
if (this.hasPollinatedLongEnough()) {
Bee.this.setHasNectar(true);
}
this.pollinating = false;
Bee.this.navigation.stop();
Bee.this.remainingCooldownBeforeLocatingNewFlower = 200;
}
@Override
public boolean requiresUpdateEveryTick() {
return true;
}
@Override
public void tick() {
if (!Bee.this.hasSavedFlowerPos()) {
return;
}
++this.pollinatingTicks;
if (this.pollinatingTicks > 600) {
Bee.this.dropFlower();
this.pollinating = false;
Bee.this.remainingCooldownBeforeLocatingNewFlower = 200;
return;
}
Vec3 flowerPos = Vec3.atBottomCenterOf(Bee.this.savedFlowerPos).add(0.0, 0.6f, 0.0);
if (flowerPos.distanceTo(Bee.this.position()) > 1.0) {
this.hoverPos = flowerPos;
this.setWantedPos();
return;
}
if (this.hoverPos == null) {
this.hoverPos = flowerPos;
}
boolean arrivedAtHoverPos = Bee.this.position().distanceTo(this.hoverPos) <= 0.1;
boolean shouldSetWantedPos = true;
if (!arrivedAtHoverPos && this.pollinatingTicks > 600) {
Bee.this.dropFlower();
return;
}
if (arrivedAtHoverPos) {
boolean shouldChangeHoverPositions;
boolean bl = shouldChangeHoverPositions = Bee.this.random.nextInt(25) == 0;
if (shouldChangeHoverPositions) {
this.hoverPos = new Vec3(flowerPos.x() + (double)this.getOffset(), flowerPos.y(), flowerPos.z() + (double)this.getOffset());
Bee.this.navigation.stop();
} else {
shouldSetWantedPos = false;
}
Bee.this.getLookControl().setLookAt(flowerPos.x(), flowerPos.y(), flowerPos.z());
}
if (shouldSetWantedPos) {
this.setWantedPos();
}
++this.successfulPollinatingTicks;
if (Bee.this.random.nextFloat() < 0.05f && this.successfulPollinatingTicks > this.lastSoundPlayedTick + 60) {
this.lastSoundPlayedTick = this.successfulPollinatingTicks;
Bee.this.playSound(SoundEvents.BEE_POLLINATE, 1.0f, 1.0f);
}
}
private void setWantedPos() {
Bee.this.getMoveControl().setWantedPosition(this.hoverPos.x(), this.hoverPos.y(), this.hoverPos.z(), 0.35f);
}
private float getOffset() {
return (Bee.this.random.nextFloat() * 2.0f - 1.0f) * 0.33333334f;
}
private Optional<BlockPos> findNearbyFlower() {
Iterable<BlockPos> closestNearbyFlowers = BlockPos.withinManhattan(Bee.this.blockPosition(), 5, 5, 5);
Long2LongOpenHashMap tempCache = new Long2LongOpenHashMap();
for (BlockPos pos : closestNearbyFlowers) {
long unreachableUntilTime = this.unreachableFlowerCache.getOrDefault(pos.asLong(), Long.MIN_VALUE);
if (Bee.this.level().getGameTime() < unreachableUntilTime) {
tempCache.put(pos.asLong(), unreachableUntilTime);
continue;
}
if (!Bee.attractsBees(Bee.this.level().getBlockState(pos))) continue;
Path path = Bee.this.navigation.createPath(pos, 1);
if (path != null && path.canReach()) {
return Optional.of(pos);
}
tempCache.put(pos.asLong(), Bee.this.level().getGameTime() + 600L);
}
this.unreachableFlowerCache = tempCache;
return Optional.empty();
}
}
private class BeeLocateHiveGoal
extends BaseBeeGoal {
private BeeLocateHiveGoal() {
}
@Override
public boolean canBeeUse() {
return Bee.this.remainingCooldownBeforeLocatingNewHive == 0 && !Bee.this.hasHive() && Bee.this.wantsToEnterHive();
}
@Override
public boolean canBeeContinueToUse() {
return false;
}
@Override
public void start() {
Bee.this.remainingCooldownBeforeLocatingNewHive = 200;
List<BlockPos> hivesWithSpace = this.findNearbyHivesWithSpace();
if (hivesWithSpace.isEmpty()) {
return;
}
for (BlockPos posToCheck : hivesWithSpace) {
if (Bee.this.goToHiveGoal.isTargetBlacklisted(posToCheck)) continue;
Bee.this.hivePos = posToCheck;
return;
}
Bee.this.goToHiveGoal.clearBlacklist();
Bee.this.hivePos = hivesWithSpace.get(0);
}
private List<BlockPos> findNearbyHivesWithSpace() {
BlockPos beePos = Bee.this.blockPosition();
PoiManager poiManager = ((ServerLevel)Bee.this.level()).getPoiManager();
Stream<PoiRecord> nearbyHives = poiManager.getInRange(p -> p.is(PoiTypeTags.BEE_HOME), beePos, 20, PoiManager.Occupancy.ANY);
return nearbyHives.map(PoiRecord::getPos).filter(Bee.this::doesHiveHaveSpace).sorted(Comparator.comparingDouble(pos -> pos.distSqr(beePos))).collect(Collectors.toList());
}
}
@VisibleForDebug
public class BeeGoToHiveGoal
extends BaseBeeGoal {
public static final int MAX_TRAVELLING_TICKS = 2400;
private int travellingTicks;
private static final int MAX_BLACKLISTED_TARGETS = 3;
private final List<BlockPos> blacklistedTargets;
private @Nullable Path lastPath;
private static final int TICKS_BEFORE_HIVE_DROP = 60;
private int ticksStuck;
BeeGoToHiveGoal() {
this.blacklistedTargets = Lists.newArrayList();
this.setFlags(EnumSet.of(Goal.Flag.MOVE));
}
@Override
public boolean canBeeUse() {
return Bee.this.hivePos != null && !Bee.this.isTooFarAway(Bee.this.hivePos) && !Bee.this.hasHome() && Bee.this.wantsToEnterHive() && !this.hasReachedTarget(Bee.this.hivePos) && Bee.this.level().getBlockState(Bee.this.hivePos).is(BlockTags.BEEHIVES);
}
@Override
public boolean canBeeContinueToUse() {
return this.canBeeUse();
}
@Override
public void start() {
this.travellingTicks = 0;
this.ticksStuck = 0;
super.start();
}
@Override
public void stop() {
this.travellingTicks = 0;
this.ticksStuck = 0;
Bee.this.navigation.stop();
Bee.this.navigation.resetMaxVisitedNodesMultiplier();
}
@Override
public void tick() {
if (Bee.this.hivePos == null) {
return;
}
++this.travellingTicks;
if (this.travellingTicks > this.adjustedTickDelay(2400)) {
this.dropAndBlacklistHive();
return;
}
if (Bee.this.navigation.isInProgress()) {
return;
}
if (Bee.this.closerThan(Bee.this.hivePos, 16)) {
boolean canReachAllTheWayToTarget = this.pathfindDirectlyTowards(Bee.this.hivePos);
if (!canReachAllTheWayToTarget) {
this.dropAndBlacklistHive();
} else if (this.lastPath != null && Bee.this.navigation.getPath().sameAs(this.lastPath)) {
++this.ticksStuck;
if (this.ticksStuck > 60) {
Bee.this.dropHive();
this.ticksStuck = 0;
}
} else {
this.lastPath = Bee.this.navigation.getPath();
}
return;
}
if (Bee.this.isTooFarAway(Bee.this.hivePos)) {
Bee.this.dropHive();
return;
}
Bee.this.pathfindRandomlyTowards(Bee.this.hivePos);
}
private boolean pathfindDirectlyTowards(BlockPos targetPos) {
int closeEnough = Bee.this.closerThan(targetPos, 3) ? 1 : 2;
Bee.this.navigation.setMaxVisitedNodesMultiplier(10.0f);
Bee.this.navigation.moveTo(targetPos.getX(), targetPos.getY(), targetPos.getZ(), closeEnough, 1.0);
return Bee.this.navigation.getPath() != null && Bee.this.navigation.getPath().canReach();
}
private boolean isTargetBlacklisted(BlockPos targetPos) {
return this.blacklistedTargets.contains(targetPos);
}
private void blacklistTarget(BlockPos targetPos) {
this.blacklistedTargets.add(targetPos);
while (this.blacklistedTargets.size() > 3) {
this.blacklistedTargets.remove(0);
}
}
private void clearBlacklist() {
this.blacklistedTargets.clear();
}
private void dropAndBlacklistHive() {
if (Bee.this.hivePos != null) {
this.blacklistTarget(Bee.this.hivePos);
}
Bee.this.dropHive();
}
private boolean hasReachedTarget(BlockPos targetPos) {
if (Bee.this.closerThan(targetPos, 2)) {
return true;
}
Path path = Bee.this.navigation.getPath();
return path != null && path.getTarget().equals(targetPos) && path.canReach() && path.isDone();
}
}
public class BeeGoToKnownFlowerGoal
extends BaseBeeGoal {
private static final int MAX_TRAVELLING_TICKS = 2400;
private int travellingTicks;
BeeGoToKnownFlowerGoal() {
this.setFlags(EnumSet.of(Goal.Flag.MOVE));
}
@Override
public boolean canBeeUse() {
return Bee.this.savedFlowerPos != null && !Bee.this.hasHome() && this.wantsToGoToKnownFlower() && !Bee.this.closerThan(Bee.this.savedFlowerPos, 2);
}
@Override
public boolean canBeeContinueToUse() {
return this.canBeeUse();
}
@Override
public void start() {
this.travellingTicks = 0;
super.start();
}
@Override
public void stop() {
this.travellingTicks = 0;
Bee.this.navigation.stop();
Bee.this.navigation.resetMaxVisitedNodesMultiplier();
}
@Override
public void tick() {
if (Bee.this.savedFlowerPos == null) {
return;
}
++this.travellingTicks;
if (this.travellingTicks > this.adjustedTickDelay(2400)) {
Bee.this.dropFlower();
return;
}
if (Bee.this.navigation.isInProgress()) {
return;
}
if (Bee.this.isTooFarAway(Bee.this.savedFlowerPos)) {
Bee.this.dropFlower();
return;
}
Bee.this.pathfindRandomlyTowards(Bee.this.savedFlowerPos);
}
private boolean wantsToGoToKnownFlower() {
return Bee.this.ticksWithoutNectarSinceExitingHive > 600;
}
}
private class BeeGrowCropGoal
extends BaseBeeGoal {
static final int GROW_CHANCE = 30;
private BeeGrowCropGoal() {
}
@Override
public boolean canBeeUse() {
if (Bee.this.getCropsGrownSincePollination() >= 10) {
return false;
}
if (Bee.this.random.nextFloat() < 0.3f) {
return false;
}
return Bee.this.hasNectar() && Bee.this.isHiveValid();
}
@Override
public boolean canBeeContinueToUse() {
return this.canBeeUse();
}
@Override
public void tick() {
if (Bee.this.random.nextInt(this.adjustedTickDelay(30)) != 0) {
return;
}
for (int i = 1; i <= 2; ++i) {
BonemealableBlock bonemealableBlock;
BlockPos belowPos = Bee.this.blockPosition().below(i);
BlockState belowState = Bee.this.level().getBlockState(belowPos);
Block belowBlock = belowState.getBlock();
BlockState growState = null;
if (!belowState.is(BlockTags.BEE_GROWABLES)) continue;
if (belowBlock instanceof CropBlock) {
CropBlock cropBlockBelow = (CropBlock)belowBlock;
if (!cropBlockBelow.isMaxAge(belowState)) {
growState = cropBlockBelow.getStateForAge(cropBlockBelow.getAge(belowState) + 1);
}
} else if (belowBlock instanceof StemBlock) {
int age = belowState.getValue(StemBlock.AGE);
if (age < 7) {
growState = (BlockState)belowState.setValue(StemBlock.AGE, age + 1);
}
} else if (belowState.is(Blocks.SWEET_BERRY_BUSH)) {
int age = belowState.getValue(SweetBerryBushBlock.AGE);
if (age < 3) {
growState = (BlockState)belowState.setValue(SweetBerryBushBlock.AGE, age + 1);
}
} else if ((belowState.is(Blocks.CAVE_VINES) || belowState.is(Blocks.CAVE_VINES_PLANT)) && (bonemealableBlock = (BonemealableBlock)((Object)belowState.getBlock())).isValidBonemealTarget(Bee.this.level(), belowPos, belowState)) {
bonemealableBlock.performBonemeal((ServerLevel)Bee.this.level(), Bee.this.random, belowPos, belowState);
growState = Bee.this.level().getBlockState(belowPos);
}
if (growState == null) continue;
Bee.this.level().levelEvent(2011, belowPos, 15);
Bee.this.level().setBlockAndUpdate(belowPos, growState);
Bee.this.incrementNumCropsGrownSincePollination();
}
}
}
private class BeeWanderGoal
extends Goal {
BeeWanderGoal() {
this.setFlags(EnumSet.of(Goal.Flag.MOVE));
}
@Override
public boolean canUse() {
return Bee.this.navigation.isDone() && Bee.this.random.nextInt(10) == 0;
}
@Override
public boolean canContinueToUse() {
return Bee.this.navigation.isInProgress();
}
@Override
public void start() {
Vec3 targetPos = this.findPos();
if (targetPos != null) {
Bee.this.navigation.moveTo(Bee.this.navigation.createPath(BlockPos.containing(targetPos), 1), 1.0);
}
}
private @Nullable Vec3 findPos() {
Vec3 wanderDirection;
if (Bee.this.isHiveValid() && !Bee.this.closerThan(Bee.this.hivePos, this.getWanderThreshold())) {
Vec3 hivePosVec = Vec3.atCenterOf(Bee.this.hivePos);
wanderDirection = hivePosVec.subtract(Bee.this.position()).normalize();
} else {
wanderDirection = Bee.this.getViewVector(0.0f);
}
int xzDist = 8;
Vec3 groundBasedPosition = HoverRandomPos.getPos(Bee.this, 8, 7, wanderDirection.x, wanderDirection.z, 1.5707964f, 3, 1);
if (groundBasedPosition != null) {
return groundBasedPosition;
}
return AirAndWaterRandomPos.getPos(Bee.this, 8, 4, -2, wanderDirection.x, wanderDirection.z, 1.5707963705062866);
}
private int getWanderThreshold() {
int distanceReduction = Bee.this.hasHive() || Bee.this.hasSavedFlowerPos() ? 24 : 16;
return 48 - distanceReduction;
}
}
private class BeeHurtByOtherGoal
extends HurtByTargetGoal {
BeeHurtByOtherGoal(Bee bee2) {
super(bee2, new Class[0]);
}
@Override
public boolean canContinueToUse() {
return Bee.this.isAngry() && super.canContinueToUse();
}
@Override
protected void alertOther(Mob other, LivingEntity hurtByMob) {
if (other instanceof Bee && this.mob.hasLineOfSight(hurtByMob)) {
other.setTarget(hurtByMob);
}
}
}
private static class BeeBecomeAngryTargetGoal
extends NearestAttackableTargetGoal<Player> {
BeeBecomeAngryTargetGoal(Bee bee) {
super(bee, Player.class, 10, true, false, bee::isAngryAt);
}
@Override
public boolean canUse() {
return this.beeCanTarget() && super.canUse();
}
@Override
public boolean canContinueToUse() {
boolean beeCanTarget = this.beeCanTarget();
if (!beeCanTarget || this.mob.getTarget() == null) {
this.targetMob = null;
return false;
}
return super.canContinueToUse();
}
private boolean beeCanTarget() {
Bee bee = (Bee)this.mob;
return bee.isAngry() && !bee.hasStung();
}
}
private abstract class BaseBeeGoal
extends Goal {
private BaseBeeGoal() {
}
public abstract boolean canBeeUse();
public abstract boolean canBeeContinueToUse();
@Override
public boolean canUse() {
return this.canBeeUse() && !Bee.this.isAngry();
}
@Override
public boolean canContinueToUse() {
return this.canBeeContinueToUse() && !Bee.this.isAngry();
}
}
}