minecraft-src/net/minecraft/world/entity/ai/goal/MeleeAttackGoal.java
2025-07-04 03:45:38 +03:00

155 lines
4.9 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.ai.goal.Goal.Flag;
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 mob, double speedModifier, boolean followingTargetEvenIfNotSeen) {
this.mob = mob;
this.speedModifier = speedModifier;
this.followingTargetEvenIfNotSeen = followingTargetEvenIfNotSeen;
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
}
@Override
public boolean canUse() {
long l = this.mob.level().getGameTime();
if (l - this.lastCanUseCheck < 20L) {
return false;
} else {
this.lastCanUseCheck = l;
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 d = this.mob.distanceToSqr(livingEntity);
if (d > 1024.0) {
this.ticksUntilNextPathRecalculation += 10;
} else if (d > 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 target) {
if (this.canPerformAttack(target)) {
this.resetAttackCooldown();
this.mob.swing(InteractionHand.MAIN_HAND);
this.mob.doHurtTarget(getServerLevel(this.mob), target);
}
}
protected void resetAttackCooldown() {
this.ticksUntilNextAttack = this.adjustedTickDelay(20);
}
protected boolean isTimeToAttack() {
return this.ticksUntilNextAttack <= 0;
}
protected boolean canPerformAttack(LivingEntity entity) {
return this.isTimeToAttack() && this.mob.isWithinMeleeAttackRange(entity) && this.mob.getSensing().hasLineOfSight(entity);
}
protected int getTicksUntilNextAttack() {
return this.ticksUntilNextAttack;
}
protected int getAttackInterval() {
return this.adjustedTickDelay(20);
}
}