package net.minecraft.world.entity.ai.goal.target; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.targeting.TargetingConditions; import net.minecraft.world.level.pathfinder.Node; import net.minecraft.world.level.pathfinder.Path; import net.minecraft.world.scores.Team; import org.jetbrains.annotations.Nullable; public abstract class TargetGoal extends Goal { private static final int EMPTY_REACH_CACHE = 0; private static final int CAN_REACH_CACHE = 1; private static final int CANT_REACH_CACHE = 2; /** * The entity that this goal belongs to */ protected final Mob mob; protected final boolean mustSee; private final boolean mustReach; private int reachCache; private int reachCacheTime; private int unseenTicks; @Nullable protected LivingEntity targetMob; protected int unseenMemoryTicks = 60; public TargetGoal(Mob mob, boolean mustSee) { this(mob, mustSee, false); } public TargetGoal(Mob mob, boolean mustSee, boolean mustReach) { this.mob = mob; this.mustSee = mustSee; this.mustReach = mustReach; } @Override public boolean canContinueToUse() { LivingEntity livingEntity = this.mob.getTarget(); if (livingEntity == null) { livingEntity = this.targetMob; } if (livingEntity == null) { return false; } else if (!this.mob.canAttack(livingEntity)) { return false; } else { Team team = this.mob.getTeam(); Team team2 = livingEntity.getTeam(); if (team != null && team2 == team) { return false; } else { double d = this.getFollowDistance(); if (this.mob.distanceToSqr(livingEntity) > d * d) { return false; } else { if (this.mustSee) { if (this.mob.getSensing().hasLineOfSight(livingEntity)) { this.unseenTicks = 0; } else if (++this.unseenTicks > reducedTickDelay(this.unseenMemoryTicks)) { return false; } } this.mob.setTarget(livingEntity); return true; } } } } protected double getFollowDistance() { return this.mob.getAttributeValue(Attributes.FOLLOW_RANGE); } @Override public void start() { this.reachCache = 0; this.reachCacheTime = 0; this.unseenTicks = 0; } @Override public void stop() { this.mob.setTarget(null); this.targetMob = null; } /** * Checks if this is a suitable target. */ protected boolean canAttack(@Nullable LivingEntity potentialTarget, TargetingConditions targetPredicate) { if (potentialTarget == null) { return false; } else if (!targetPredicate.test(getServerLevel(this.mob), this.mob, potentialTarget)) { return false; } else if (!this.mob.isWithinRestriction(potentialTarget.blockPosition())) { return false; } else { if (this.mustReach) { if (--this.reachCacheTime <= 0) { this.reachCache = 0; } if (this.reachCache == 0) { this.reachCache = this.canReach(potentialTarget) ? 1 : 2; } if (this.reachCache == 2) { return false; } } return true; } } /** * Checks to see if this entity can find a short path to the given target. */ private boolean canReach(LivingEntity target) { this.reachCacheTime = reducedTickDelay(10 + this.mob.getRandom().nextInt(5)); Path path = this.mob.getNavigation().createPath(target, 0); if (path == null) { return false; } else { Node node = path.getEndNode(); if (node == null) { return false; } else { int i = node.x - target.getBlockX(); int j = node.z - target.getBlockZ(); return i * i + j * j <= 2.25; } } } public TargetGoal setUnseenMemoryTicks(int unseenMemoryTicks) { this.unseenMemoryTicks = unseenMemoryTicks; return this; } }