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 var0, int var1) { return createPath(var0, 8, false, var1); } @Override protected Path createPath(Set 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 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 var0, int var1) { return createPath(var0.collect(Collectors. 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; } } } } }