154 lines
5.7 KiB
Java
154 lines
5.7 KiB
Java
package net.minecraft.world.entity.ai.goal;
|
|
|
|
import java.util.EnumSet;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.entity.EntitySelector;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.PathfinderMob;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.pathfinder.Path;
|
|
|
|
public class MeleeAttackGoal extends Goal {
|
|
protected final PathfinderMob mob;
|
|
private final double speedModifier;
|
|
private final boolean followingTargetEvenIfNotSeen;
|
|
private Path path;
|
|
private double pathedTargetX;
|
|
private double pathedTargetY;
|
|
private double pathedTargetZ;
|
|
private int ticksUntilNextPathRecalculation;
|
|
private int ticksUntilNextAttack;
|
|
private final int attackInterval = 20;
|
|
private long lastCanUseCheck;
|
|
private static final long COOLDOWN_BETWEEN_CAN_USE_CHECKS = 20L;
|
|
|
|
public MeleeAttackGoal(PathfinderMob p_25552_, double p_25553_, boolean p_25554_) {
|
|
this.mob = p_25552_;
|
|
this.speedModifier = p_25553_;
|
|
this.followingTargetEvenIfNotSeen = p_25554_;
|
|
this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
long i = this.mob.level().getGameTime();
|
|
if (i - this.lastCanUseCheck < 20L) {
|
|
return false;
|
|
} else {
|
|
this.lastCanUseCheck = i;
|
|
LivingEntity livingentity = this.mob.getTarget();
|
|
if (livingentity == null) {
|
|
return false;
|
|
} else if (!livingentity.isAlive()) {
|
|
return false;
|
|
} else {
|
|
this.path = this.mob.getNavigation().createPath(livingentity, 0);
|
|
return this.path != null ? true : this.mob.isWithinMeleeAttackRange(livingentity);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
LivingEntity livingentity = this.mob.getTarget();
|
|
if (livingentity == null) {
|
|
return false;
|
|
} else if (!livingentity.isAlive()) {
|
|
return false;
|
|
} else if (!this.followingTargetEvenIfNotSeen) {
|
|
return !this.mob.getNavigation().isDone();
|
|
} else {
|
|
return !this.mob.isWithinRestriction(livingentity.blockPosition())
|
|
? false
|
|
: !(livingentity instanceof Player player && (player.isSpectator() || player.isCreative()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
this.mob.getNavigation().moveTo(this.path, this.speedModifier);
|
|
this.mob.setAggressive(true);
|
|
this.ticksUntilNextPathRecalculation = 0;
|
|
this.ticksUntilNextAttack = 0;
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
LivingEntity livingentity = this.mob.getTarget();
|
|
if (!EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(livingentity)) {
|
|
this.mob.setTarget(null);
|
|
}
|
|
|
|
this.mob.setAggressive(false);
|
|
this.mob.getNavigation().stop();
|
|
}
|
|
|
|
@Override
|
|
public boolean requiresUpdateEveryTick() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
LivingEntity livingentity = this.mob.getTarget();
|
|
if (livingentity != null) {
|
|
this.mob.getLookControl().setLookAt(livingentity, 30.0F, 30.0F);
|
|
this.ticksUntilNextPathRecalculation = Math.max(this.ticksUntilNextPathRecalculation - 1, 0);
|
|
if ((this.followingTargetEvenIfNotSeen || this.mob.getSensing().hasLineOfSight(livingentity))
|
|
&& this.ticksUntilNextPathRecalculation <= 0
|
|
&& (
|
|
this.pathedTargetX == 0.0 && this.pathedTargetY == 0.0 && this.pathedTargetZ == 0.0
|
|
|| livingentity.distanceToSqr(this.pathedTargetX, this.pathedTargetY, this.pathedTargetZ) >= 1.0
|
|
|| this.mob.getRandom().nextFloat() < 0.05F
|
|
)) {
|
|
this.pathedTargetX = livingentity.getX();
|
|
this.pathedTargetY = livingentity.getY();
|
|
this.pathedTargetZ = livingentity.getZ();
|
|
this.ticksUntilNextPathRecalculation = 4 + this.mob.getRandom().nextInt(7);
|
|
double d0 = this.mob.distanceToSqr(livingentity);
|
|
if (d0 > 1024.0) {
|
|
this.ticksUntilNextPathRecalculation += 10;
|
|
} else if (d0 > 256.0) {
|
|
this.ticksUntilNextPathRecalculation += 5;
|
|
}
|
|
|
|
if (!this.mob.getNavigation().moveTo(livingentity, this.speedModifier)) {
|
|
this.ticksUntilNextPathRecalculation += 15;
|
|
}
|
|
|
|
this.ticksUntilNextPathRecalculation = this.adjustedTickDelay(this.ticksUntilNextPathRecalculation);
|
|
}
|
|
|
|
this.ticksUntilNextAttack = Math.max(this.ticksUntilNextAttack - 1, 0);
|
|
this.checkAndPerformAttack(livingentity);
|
|
}
|
|
}
|
|
|
|
protected void checkAndPerformAttack(LivingEntity p_25557_) {
|
|
if (this.canPerformAttack(p_25557_)) {
|
|
this.resetAttackCooldown();
|
|
this.mob.swing(InteractionHand.MAIN_HAND);
|
|
this.mob.doHurtTarget(getServerLevel(this.mob), p_25557_);
|
|
}
|
|
}
|
|
|
|
protected void resetAttackCooldown() {
|
|
this.ticksUntilNextAttack = this.adjustedTickDelay(20);
|
|
}
|
|
|
|
protected boolean isTimeToAttack() {
|
|
return this.ticksUntilNextAttack <= 0;
|
|
}
|
|
|
|
protected boolean canPerformAttack(LivingEntity p_301160_) {
|
|
return this.isTimeToAttack() && this.mob.isWithinMeleeAttackRange(p_301160_) && this.mob.getSensing().hasLineOfSight(p_301160_);
|
|
}
|
|
|
|
protected int getTicksUntilNextAttack() {
|
|
return this.ticksUntilNextAttack;
|
|
}
|
|
|
|
protected int getAttackInterval() {
|
|
return this.adjustedTickDelay(20);
|
|
}
|
|
} |