 SoundEvent getHurtSound(DamageSource damageSourceIn) {
        return MMOSoundEvents.ENTITY_BOLETUS_HURT;
    }

    @Override
    protected SoundEvent getDeathSound() {
        return MMOSoundEvents.ENTITY_BOLETUS_DEATH;
    }

    public void writeAdditional(CompoundNBT compound) {
        super.writeAdditional(compound);
        this.writeAngerNBT(compound);
    }

    public void readAdditional(CompoundNBT compound) {
        super.readAdditional(compound);
        if (!world.isRemote) //FORGE: allow this entity to be read from nbt on client. (Fixes MC-189565)
            this.readAngerNBT((ServerWorld) this.world, compound);
    }

    public boolean attackEntityFrom(DamageSource source, float amount) {
        return!this.isInvulnerableTo(source) && super.attackEntityFrom(source, amount);
    }

    /**
     * Player tries to sleep. Check if this entity is angry or if the player can sleep.
     */
    public boolean func_230292_f_(PlayerEntity p_230292_1_) {
        return this.func_233680_b_(p_230292_1_);
    }

    private <E extends IAnimatable> PlayState idlePredicate(AnimationEvent<E> event) {
        event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.boletus.idle", true));
        return PlayState.CONTINUE;
    }

    private <E extends IAnimatable> PlayState puffPredicate(AnimationEvent<E> event) {
        event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.boletus.puff"));
        return puffAnimationEnabled? PlayState.CONTINUE : PlayState.STOP;
    }

    private <E extends IAnimatable> PlayState attackPredicate(AnimationEvent<E> event) {
        event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.boletus.attack"));
        return attackAnimationEnabled? PlayState.CONTINUE : PlayState.STOP;
    }

    @Override
    public void registerControllers(AnimationData data) {
        data.addAnimationController(new AnimationController<>(this, "idle", 0, this::idlePredicate));
        data.addAnimationController(new AnimationController<>(this, "puff", 0, this::puffPredicate));
        data.addAnimationController(new AnimationController<>(this, "attack", 0, this::attackPredicate));
    }

    @Override
    public AnimationFactory getFactory() {
        return this.factory;
    }

    @Override
    public void onAnimation(short animationId) {
        if (animationId == MMOEntityAnimations.BOLETUS_PUFF) {
            AnimationData data = this.factory.getOrCreateAnimationData(this.getEntityId());
            AnimationController<?> controller = data.getAnimationControllers().get("puff");
            controller.setAnimation(new AnimationBuilder().addAnimation("animation.boletus.puff"));
            controller.markNeedsReload();
            this.puffAnimationEnabled = true;
        }
    }

    public static class BoletusMeleeAttackGoal extends Goal {

        protected final CreatureEntity attacker;
        private final double speedTowardsTarget;
        private final boolean longMemory;
        private Path path;
        private double targetX;
        private double targetY;
        private double targetZ;
        private int delayCounter;
        private int swingCooldown;
        private final int attackInterval = 20;
        private long lastCheckTime;
        private int failedPathFindingPenalty = 0;
        private boolean canPenalize = false;

        public BoletusMeleeAttackGoal(CreatureEntity creature, double speedIn, boolean useLongMemory) {
            this.attacker = creature;
            this.speedTowardsTarget = speedIn;
            this.longMemory = useLongMemory;
            this.setMutexFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
        }

        /**
         * Returns whether execution should begin. You can also read and cache any state necessary for execution in this
         * method as well.
         */
        public boolean shouldExecute() {
            long i = this.attacker.world.getGameTime();
            if (i - this.lastCheckTime < 20L) {
                return false;
            } else {
                this.lastCheckTime = i;
                LivingEntity livingentity = this.attacker.getAttackTarget();
                if (livingentity == null) {
                    return false;
                } else if (!livingentity.isAlive()) {
                    return false;
                } else {
                    if (canPenalize) {
                        if (--this.delayCounter <= 0) {
                            this.path = this.attacker.getNavigator().pathfind(livingentity, 0);
                            this.delayCounter = 4 + this.attacker.getRNG().nextInt(7);
                            return this.path!= null;
                        } else {
                            return true;
                        }
                    }
                    this.path = this.attacker.getNavigator().pathfind(livingentity, 0);
                    if (this.path!= null) {
                        return true;
                    } else {
                        return this.getAttackReachSqr(livingentity) >= this.attacker.getDistanceSq(livingentity.getPosX(), livingentity.getPosY(), livingentity.getPosZ());
                    }
                }
            }
        }

        /**
         * Returns whether an in-progress EntityAIBase should continue executing
         */
        public boolean shouldContinueExecuting() {
            LivingEntity livingentity = this.attacker.getAttackTarget();
            if (livingentity == null) {
                return false;
            } else if (!livingentity.isAlive()) {
                return false;
            } else if (!this.longMemory) {
                return!this.attacker.getNavigator().noPath();
            } else if (!this.attacker.isWithinHomeDistanceFromPosition(livingentity.getPosition())) {
                return false;
            } else {
                return!(livingentity instanceof PlayerEntity) ||!livingentity.isSpectator() &&!((PlayerEntity)livingentity).isCreative();
            }
        }

        /**
         * Execute a one shot task or start executing a continuous task
         */
        public void startExecuting() {
            this.attacker.getNavigator().setPath(this.path, this.speedTowardsTarget);
            this.attacker.setAggroed(true);
            this.delayCounter = 0;
            this.swingCooldown = 0;
        }

        /**
         * Reset the task's internal state. Called when this task is interrupted by another one
         */
        public void resetTask() {
            LivingEntity livingentity = this.attacker.getAttackTarget();
            if (!EntityPredicates.CAN_AI_TARGET.test(livingentity)) {
                this.attacker.setAttackTarget((LivingEntity)null);
            }

            this.attacker.setAggroed(false);
            this.attacker.getNavigator().clearPath();
        }

        /**
         * Keep ticking a continuous task that has already been started
         */
        public void tick() {
            LivingEntity livingentity = this.attacker.getAttackTarget();
            if(livingentity == null) return;

            this.attacker.getLookController().setLookPositionWithEntity(livingentity, 30.0F, 30.0F);
            double d0 = this.attacker.getDistanceSq(livingentity.getPosX(), livingentity.getPosY(), livingentity.getPosZ());
            this.delayCounter = Math.max(this.delayCounter - 1, 0);
            if ((this.longMemory || this.attacker.getEntitySenses().canSee(livingentity)) && this.delayCounter <= 0 && (this.targetX == 0.0D && this.targetY == 0.0D && this.targetZ == 0.0D || livingentity.getDistanceSq(this.targetX, this.targetY, this.targetZ) >= 1.0D || this.attacker.getRNG().nextFloat() < 0.05F)) {
                this.targetX = livingentity.getPosX();
                this.targetY = livingentity.getPosY();
                this.targetZ = livingentity.getPosZ();
                this.