Code/net/minecraft/world/entity/ai/goal/MeleeAttackGoal.java

154 lines
5.7 KiB
Java
Raw Normal View History

2025-07-01 06:20:03 +00:00
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);
}
}