154 lines
4.9 KiB
Java
154 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.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(Goal.Flag.MOVE, Goal.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) || !livingEntity.isSpectator() && !((Player)livingEntity).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(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);
|
|
}
|
|
}
|