Citizens2/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/EntityNavigation.java

573 lines
20 KiB
Java

package net.citizensnpcs.nms.v1_19_R3.util;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableSet;
import net.citizensnpcs.Settings;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.monster.Slime;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.PathNavigationRegion;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.Node;
import net.minecraft.world.level.pathfinder.NodeEvaluator;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.level.pathfinder.PathFinder;
import net.minecraft.world.phys.Vec3;
public class EntityNavigation extends PathNavigation {
private boolean avoidSun;
private final AttributeInstance followRange;
protected boolean hasDelayedRecomputation;
private boolean isStuck;
protected int lastStuckCheck;
protected Vec3 lastStuckCheckPos = Vec3.ZERO;
protected long lastTimeoutCheck;
protected float maxDistanceToWaypoint = 0.5F;
private float maxVisitedNodesMultiplier = 1.0F;
private final LivingEntity mob;
private final MobAI mvmt;
protected EntityNodeEvaluator nodeEvaluator;
protected Path path;
private final EntityPathfinder pathFinder;
private int reachRange;
protected double speedModifier;
private BlockPos targetPos;
protected int tick;
protected long timeLastRecompute;
protected Vec3i timeoutCachedNode = Vec3i.ZERO;
protected double timeoutLimit;
protected long timeoutTimer;
public EntityNavigation(LivingEntity entityinsentient, Level world) {
super(new Slime(EntityType.SLIME, world), world);
this.mob = entityinsentient;
this.mvmt = MobAI.from(entityinsentient);
this.followRange = entityinsentient.getAttribute(Attributes.FOLLOW_RANGE);
this.nodeEvaluator = new EntityNodeEvaluator();
this.nodeEvaluator.setCanPassDoors(true);
this.pathFinder = new EntityPathfinder(this.nodeEvaluator, Settings.Setting.MAXIMUM_VISITED_NODES.asInt());
this.setRange(24);
}
@Override
public boolean canCutCorner(BlockPathTypes pathtype) {
return (pathtype != BlockPathTypes.DANGER_FIRE && pathtype != BlockPathTypes.DANGER_POWDER_SNOW
&& pathtype != BlockPathTypes.DANGER_OTHER && pathtype != BlockPathTypes.WALKABLE_DOOR);
}
@Override
public boolean canFloat() {
return this.nodeEvaluator.canFloat();
}
@Override
protected boolean canMoveDirectly(Vec3 var0, Vec3 var1) {
return false;
/*
int var5 = Mth.floor(var0.x);
int var6 = Mth.floor(var0.z);
double var7 = var1.x - var0.x;
double var9 = var1.z - var0.z;
double var11 = var7 * var7 + var9 * var9;
if (var11 < 1.0E-8D)
return false;
double var13 = 1.0D / Math.sqrt(var11);
var7 *= var13;
var9 *= var13;
var2 += 2;
var4 += 2;
if (!canWalkOn(var5, Mth.floor(var0.y), var6, var2, var3, var4, var0, var7, var9))
return false;
var2 -= 2;
var4 -= 2;
double var15 = 1.0D / Math.abs(var7);
double var17 = 1.0D / Math.abs(var9);
double var19 = var5 - var0.x;
double var21 = var6 - var0.z;
if (var7 >= 0.0D)
var19++;
if (var9 >= 0.0D)
var21++;
var19 /= var7;
var21 /= var9;
int var23 = (var7 < 0.0D) ? -1 : 1;
int var24 = (var9 < 0.0D) ? -1 : 1;
int var25 = Mth.floor(var1.x);
int var26 = Mth.floor(var1.z);
int var27 = var25 - var5;
int var28 = var26 - var6;
while (var27 * var23 > 0 || var28 * var24 > 0) {
if (var19 < var21) {
var19 += var15;
var5 += var23;
var27 = var25 - var5;
} else {
var21 += var17;
var6 += var24;
var28 = var26 - var6;
}
if (!canWalkOn(var5, Mth.floor(var0.y), var6, var2, var3, var4, var0, var7, var9))
return false;
}
return true;*/
}
public boolean canOpenDoors() {
return this.nodeEvaluator.canPassDoors();
}
public boolean canPassDoors() {
return this.nodeEvaluator.canPassDoors();
}
@Override
protected boolean canUpdatePath() {
return (this.mob.isOnGround() || isInLiquid() || this.mob.isPassenger());
}
/*
private boolean canWalkAbove(int var0, int var1, int var2, int var3, int var4, int var5, Vec3 var6, double var7,
double var9) {
for (BlockPos var12 : BlockPos.betweenClosed(new BlockPos(var0, var1, var2),
new BlockPos(var0 + var3 - 1, var1 + var4 - 1, var2 + var5 - 1))) {
double var13 = var12.getX() + 0.5D - var6.x;
double var15 = var12.getZ() + 0.5D - var6.z;
if (var13 * var7 + var15 * var9 < 0.0D)
continue;
if (!this.level.getBlockState(var12).isPathfindable(this.level, var12, PathComputationType.LAND))
return false;
}
return true;
}
private boolean canWalkOn(int var0, int var1, int var2, int var3, int var4, int var5, Vec3 var6, double var7,
double var9) {
int var11 = var0 - var3 / 2;
int var12 = var2 - var5 / 2;
if (!canWalkAbove(var11, var1, var12, var3, var4, var5, var6, var7, var9))
return false;
for (int var13 = var11; var13 < var11 + var3; var13++) {
for (int var14 = var12; var14 < var12 + var5; var14++) {
double var15 = var13 + 0.5D - var6.x;
double var17 = var14 + 0.5D - var6.z;
if (var15 * var7 + var17 * var9 >= 0.0D) {
BlockPathTypes var19 = this.nodeEvaluator.getBlockPathType(this.level, var13, var1 - 1, var14,
this.mob, var3, var4, var5, true, true);
if (!hasValidPathType(var19))
return false;
var19 = this.nodeEvaluator.getBlockPathType(this.level, var13, var1, var14, this.mob, var3, var4,
var5, true, true);
float var20 = this.mob.getPathfindingMalus(var19);
if (var20 < 0.0F || var20 >= 8.0F)
return false;
if (var19 == BlockPathTypes.DAMAGE_FIRE || var19 == BlockPathTypes.DANGER_FIRE
|| var19 == BlockPathTypes.DAMAGE_OTHER)
return false;
}
}
}
return true;
}*/ @Override
public Path createPath(BlockPos var0, int var1) {
if (this.level.getBlockState(var0).isAir()) {
BlockPos var2 = var0.below();
while (var2.getY() > this.level.getMinBuildHeight() && this.level.getBlockState(var2).isAir())
var2 = var2.below();
if (var2.getY() > this.level.getMinBuildHeight())
return supercreatePath(var2.above(), var1);
while (var2.getY() < this.level.getMaxBuildHeight() && this.level.getBlockState(var2).isAir())
var2 = var2.above();
var0 = var2;
}
if (this.level.getBlockState(var0).getMaterial().isSolid()) {
BlockPos var2 = var0.above();
while (var2.getY() < this.level.getMaxBuildHeight()
&& this.level.getBlockState(var2).getMaterial().isSolid())
var2 = var2.above();
return supercreatePath(var2, var1);
}
return supercreatePath(var0, var1);
}
@Override
public Path createPath(BlockPos var0, int var1, int var2) {
return createPath(ImmutableSet.of(var0), 8, false, var1, var2);
}
@Override
public Path createPath(Entity var0, int var1) {
return createPath(var0.blockPosition(), var1);
}
@Override
public Path createPath(Set<BlockPos> var0, int var1) {
return createPath(var0, 8, false, var1);
}
@Override
protected Path createPath(Set<BlockPos> var0, int var1, boolean var2, int var3) {
return createPath(var0, var1, var2, var3, (float) this.mob.getAttributeValue(Attributes.FOLLOW_RANGE));
}
@Override
protected Path createPath(Set<BlockPos> var0, int var1, boolean var2, int var3, float var4) {
if (var0.isEmpty() || (this.mob.getY() < this.level.getMinBuildHeight()) || !canUpdatePath())
return null;
if (this.path != null && !this.path.isDone() && var0.contains(this.targetPos))
return this.path;
this.level.getProfiler().push("pathfind");
BlockPos var5 = var2 ? this.mob.blockPosition().above() : this.mob.blockPosition();
int var6 = (int) (var4 + var1);
PathNavigationRegion var7 = new PathNavigationRegion(this.level, var5.offset(-var6, -var6, -var6),
var5.offset(var6, var6, var6));
Path var8 = this.pathFinder.findPath(var7, this.mob, var0, var4, var3, this.maxVisitedNodesMultiplier);
this.level.getProfiler().pop();
if (var8 != null && var8.getTarget() != null) {
this.targetPos = var8.getTarget();
this.reachRange = var3;
this.resetStuckTimeout();
}
return var8;
}
@Override
public Path createPath(Stream<BlockPos> var0, int var1) {
return createPath(var0.collect(Collectors.<BlockPos> toSet()), 8, false, var1);
}
@Override
protected PathFinder createPathFinder(int paramInt) {
return null;
}
@Override
protected void doStuckDetection(Vec3 var0) {
if (this.tick - this.lastStuckCheck > 100) {
if (var0.distanceToSqr(this.lastStuckCheckPos) < 2.25D) {
this.isStuck = true;
stop();
} else {
this.isStuck = false;
}
this.lastStuckCheck = this.tick;
this.lastStuckCheckPos = var0;
}
if (this.path != null && !this.path.isDone()) {
BlockPos blockPos = this.path.getNextNodePos();
if (blockPos.equals(this.timeoutCachedNode)) {
this.timeoutTimer += System.currentTimeMillis() - this.lastTimeoutCheck;
} else {
this.timeoutCachedNode = blockPos;
double var2 = var0.distanceTo(Vec3.atBottomCenterOf(this.timeoutCachedNode));
this.timeoutLimit = (this.mob.getSpeed() > 0.0F) ? (var2 / this.mob.getSpeed() * 1000.0D) : 0.0D;
}
if (this.timeoutLimit > 0.0D && this.timeoutTimer > this.timeoutLimit * 3.0D)
timeoutPath();
this.lastTimeoutCheck = System.currentTimeMillis();
}
}
@Override
protected void followThePath() {
Vec3 var0 = getTempMobPos();
this.maxDistanceToWaypoint = (this.mob.getBbWidth() > 0.75F) ? (this.mob.getBbWidth() / 2.0F)
: (0.75F - this.mob.getBbWidth() / 2.0F);
BlockPos blockPos = this.path.getNextNodePos();
double var2 = Math.abs(this.mob.getX() - (blockPos.getX() + 0.5D));
double var4 = Math.abs(this.mob.getY() - blockPos.getY());
double var6 = Math.abs(this.mob.getZ() - (blockPos.getZ() + 0.5D));
boolean var8 = (var2 < this.maxDistanceToWaypoint && var6 < this.maxDistanceToWaypoint && var4 < 1.0D);
if (var8 || (canCutCorner((this.path.getNextNode()).type) && shouldTargetNextNodeInDirection(var0)))
this.path.advance();
doStuckDetection(var0);
}
@Override
public float getMaxDistanceToWaypoint() {
return this.maxDistanceToWaypoint;
}
@Override
public NodeEvaluator getNodeEvaluator() {
return this.nodeEvaluator;
}
@Override
public Path getPath() {
return this.path;
}
public Path getPathEntity() {
return this.path;
}
private int getSurfaceY() {
if (this.mob.isInWater() && this.canFloat()) {
int var0 = this.mob.getBlockY();
BlockState var1 = this.level.getBlockState(BlockPos.containing(this.mob.getX(), var0, this.mob.getZ()));
int var2 = 0;
do {
if (!var1.is(Blocks.WATER)) {
return var0;
}
++var0;
var1 = this.level.getBlockState(BlockPos.containing(this.mob.getX(), var0, this.mob.getZ()));
++var2;
} while (var2 <= 16);
return this.mob.getBlockY();
} else {
return Mth.floor(this.mob.getY() + 0.5);
}
}
@Override
public BlockPos getTargetPos() {
return this.targetPos;
}
@Override
protected Vec3 getTempMobPos() {
return new Vec3(this.mob.getX(), getSurfaceY(), this.mob.getZ());
}
protected boolean hasValidPathType(BlockPathTypes var0) {
if (var0 == BlockPathTypes.WATER) {
return false;
} else if (var0 == BlockPathTypes.LAVA) {
return false;
} else {
return var0 != BlockPathTypes.OPEN;
}
}
@Override
public boolean isDone() {
return (this.path == null || this.path.isDone());
}
@Override
protected boolean isInLiquid() {
return (this.mob.isInWaterOrBubble() || this.mob.isInLava());
}
@Override
public boolean isInProgress() {
return !isDone();
}
@Override
public boolean isStableDestination(BlockPos var0) {
BlockPos var1 = var0.below();
return this.level.getBlockState(var1).isSolidRender(this.level, var1);
}
@Override
public boolean isStuck() {
return this.isStuck;
}
@Override
public boolean moveTo(double var0, double var2, double var4, double var6) {
return moveTo(createPath(BlockPos.containing(var0, var2, var4), 1), var6);
}
@Override
public boolean moveTo(Entity var0, double var1) {
Path var3 = createPath(var0, 1);
return (var3 != null && moveTo(var3, var1));
}
@Override
public boolean moveTo(Path var0, double var1) {
if (var0 == null) {
this.path = null;
return false;
}
if (!var0.sameAs(this.path))
this.path = var0;
if (isDone())
return false;
trimPath();
if (this.path.getNodeCount() <= 0)
return false;
this.speedModifier = var1;
Vec3 var3 = getTempMobPos();
this.lastStuckCheck = this.tick;
this.lastStuckCheckPos = var3;
return true;
}
@Override
public void recomputePath() {
if (this.level.getGameTime() - this.timeLastRecompute > 20L) {
if (this.targetPos != null) {
this.path = null;
this.path = createPath(this.targetPos, this.reachRange);
this.timeLastRecompute = this.level.getGameTime();
this.hasDelayedRecomputation = false;
}
} else {
this.hasDelayedRecomputation = true;
}
}
@Override
public void resetMaxVisitedNodesMultiplier() {
this.maxVisitedNodesMultiplier = 1.0F;
}
private void resetStuckTimeout() {
this.timeoutCachedNode = Vec3i.ZERO;
this.timeoutTimer = 0L;
this.timeoutLimit = 0.0D;
this.isStuck = false;
}
public void setAvoidSun(boolean var0) {
this.avoidSun = var0;
}
@Override
public void setCanFloat(boolean var0) {
this.nodeEvaluator.setCanFloat(var0);
}
public void setCanOpenDoors(boolean var0) {
this.nodeEvaluator.setCanOpenDoors(var0);
}
public void setCanPassDoors(boolean var0) {
this.nodeEvaluator.setCanPassDoors(var0);
}
@Override
public void setMaxVisitedNodesMultiplier(float var0) {
this.maxVisitedNodesMultiplier = var0;
}
public void setRange(float pathfindingRange) {
this.followRange.setBaseValue(pathfindingRange);
}
@Override
public void setSpeedModifier(double var0) {
this.speedModifier = var0;
}
@Override
public boolean shouldRecomputePath(BlockPos var0) {
if (this.hasDelayedRecomputation) {
return false;
} else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) {
Node var1 = this.path.getEndNode();
Vec3 var2 = new Vec3((var1.x + this.mob.getX()) / 2.0D, (var1.y + this.mob.getY()) / 2.0D,
(var1.z + this.mob.getZ()) / 2.0D);
return var0.closerToCenterThan(var2, this.path.getNodeCount() - this.path.getNextNodeIndex());
} else {
return false;
}
}
private boolean shouldTargetNextNodeInDirection(Vec3 var0) {
if (this.path.getNextNodeIndex() + 1 >= this.path.getNodeCount())
return false;
Vec3 var1 = Vec3.atBottomCenterOf(this.path.getNextNodePos());
if (!var0.closerThan(var1, 2.0D))
return false;
Vec3 var2 = Vec3.atBottomCenterOf(this.path.getNodePos(this.path.getNextNodeIndex() + 1));
Vec3 var3 = var2.subtract(var1);
Vec3 var4 = var0.subtract(var1);
return (var3.dot(var4) > 0.0D);
}
@Override
public void stop() {
this.path = null;
}
public Path supercreatePath(BlockPos var0, int var1) {
return createPath(ImmutableSet.of(var0), 8, false, var1);
}
protected void supertrimPath() {
if (this.path == null)
return;
for (int var0 = 0; var0 < this.path.getNodeCount(); ++var0) {
Node var1 = this.path.getNode(var0);
Node var2 = var0 + 1 < this.path.getNodeCount() ? this.path.getNode(var0 + 1) : null;
BlockState var3 = this.level.getBlockState(new BlockPos(var1.x, var1.y, var1.z));
if (var3.is(BlockTags.CAULDRONS)) {
this.path.replaceNode(var0, var1.cloneAndMove(var1.x, var1.y + 1, var1.z));
if (var2 != null && var1.y >= var2.y) {
this.path.replaceNode(var0 + 1, var1.cloneAndMove(var2.x, var1.y + 1, var2.z));
}
}
}
}
@Override
public void tick() {
this.tick++;
if (this.hasDelayedRecomputation) {
recomputePath();
}
if (isDone())
return;
if (canUpdatePath()) {
followThePath();
} else if (this.path != null && !this.path.isDone()) {
Vec3 vec31 = getTempMobPos();
Vec3 vec32 = this.path.getNextEntityPos(this.mob);
if (vec31.y > vec32.y && !this.mob.isOnGround() && Mth.floor(vec31.x) == Mth.floor(vec32.x)
&& Mth.floor(vec31.z) == Mth.floor(vec32.z))
this.path.advance();
}
if (isDone())
return;
Vec3 var0 = this.path.getNextEntityPos(this.mob);
mvmt.getMoveControl().setWantedPosition(var0.x, this.getGroundY(var0), var0.z, this.speedModifier);
}
private void timeoutPath() {
resetStuckTimeout();
stop();
}
@Override
protected void trimPath() {
supertrimPath();
if (this.avoidSun) {
if (this.level.canSeeSky(BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()))) {
return;
}
for (int var0 = 0; var0 < this.path.getNodeCount(); ++var0) {
Node var1 = this.path.getNode(var0);
if (this.level.canSeeSky(new BlockPos(var1.x, var1.y, var1.z))) {
this.path.truncateNodes(var0);
return;
}
}
}
}
}