package net.minecraft.world.entity.ai.goal; import java.util.EnumSet; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.monster.RangedAttackMob; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.BowItem; import net.minecraft.world.item.Items; public class RangedBowAttackGoal extends Goal { private final T mob; private final double speedModifier; private int attackIntervalMin; private final float attackRadiusSqr; private int attackTime = -1; private int seeTime; private boolean strafingClockwise; private boolean strafingBackwards; private int strafingTime = -1; public RangedBowAttackGoal(T mob, double speedModifier, int attackIntervalMin, float attackRadius) { this.mob = mob; this.speedModifier = speedModifier; this.attackIntervalMin = attackIntervalMin; this.attackRadiusSqr = attackRadius * attackRadius; this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); } public void setMinAttackInterval(int attackCooldown) { this.attackIntervalMin = attackCooldown; } @Override public boolean canUse() { return this.mob.getTarget() == null ? false : this.isHoldingBow(); } protected boolean isHoldingBow() { return this.mob.isHolding(Items.BOW); } @Override public boolean canContinueToUse() { return (this.canUse() || !this.mob.getNavigation().isDone()) && this.isHoldingBow(); } @Override public void start() { super.start(); this.mob.setAggressive(true); } @Override public void stop() { super.stop(); this.mob.setAggressive(false); this.seeTime = 0; this.attackTime = -1; this.mob.stopUsingItem(); } @Override public boolean requiresUpdateEveryTick() { return true; } @Override public void tick() { LivingEntity livingEntity = this.mob.getTarget(); if (livingEntity != null) { double d = this.mob.distanceToSqr(livingEntity.getX(), livingEntity.getY(), livingEntity.getZ()); boolean bl = this.mob.getSensing().hasLineOfSight(livingEntity); boolean bl2 = this.seeTime > 0; if (bl != bl2) { this.seeTime = 0; } if (bl) { this.seeTime++; } else { this.seeTime--; } if (!(d > this.attackRadiusSqr) && this.seeTime >= 20) { this.mob.getNavigation().stop(); this.strafingTime++; } else { this.mob.getNavigation().moveTo(livingEntity, this.speedModifier); this.strafingTime = -1; } if (this.strafingTime >= 20) { if (this.mob.getRandom().nextFloat() < 0.3) { this.strafingClockwise = !this.strafingClockwise; } if (this.mob.getRandom().nextFloat() < 0.3) { this.strafingBackwards = !this.strafingBackwards; } this.strafingTime = 0; } if (this.strafingTime > -1) { if (d > this.attackRadiusSqr * 0.75F) { this.strafingBackwards = false; } else if (d < this.attackRadiusSqr * 0.25F) { this.strafingBackwards = true; } this.mob.getMoveControl().strafe(this.strafingBackwards ? -0.5F : 0.5F, this.strafingClockwise ? 0.5F : -0.5F); if (this.mob.getControlledVehicle() instanceof Mob mob) { mob.lookAt(livingEntity, 30.0F, 30.0F); } this.mob.lookAt(livingEntity, 30.0F, 30.0F); } else { this.mob.getLookControl().setLookAt(livingEntity, 30.0F, 30.0F); } if (this.mob.isUsingItem()) { if (!bl && this.seeTime < -60) { this.mob.stopUsingItem(); } else if (bl) { int i = this.mob.getTicksUsingItem(); if (i >= 20) { this.mob.stopUsingItem(); this.mob.performRangedAttack(livingEntity, BowItem.getPowerForTime(i)); this.attackTime = this.attackIntervalMin; } } } else if (--this.attackTime <= 0 && this.seeTime >= -60) { this.mob.startUsingItem(ProjectileUtil.getWeaponHoldingHand(this.mob, Items.BOW)); } } } }