From a79ba3a0e479e810f259d70869a54407b6250b7a Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 23 Feb 2026 04:38:02 -0500 Subject: [PATCH] Smoother + Most-Cost Effective Flight Pathing --- .../npcs/ai/pathfinder/FlyPathFinder.java | 21 +++- .../ai/pathfinder/PathNavigateFlying.java | 101 ++++++++++++++++-- .../noppes/npcs/entity/EntityNPCFlying.java | 6 +- 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/src/main/java/noppes/npcs/ai/pathfinder/FlyPathFinder.java b/src/main/java/noppes/npcs/ai/pathfinder/FlyPathFinder.java index cc5758ce6..af80c4a28 100644 --- a/src/main/java/noppes/npcs/ai/pathfinder/FlyPathFinder.java +++ b/src/main/java/noppes/npcs/ai/pathfinder/FlyPathFinder.java @@ -173,7 +173,8 @@ public class FlyPathFinder extends PathFinder { for (int j = 0; j < i; ++j) { NPCPathPoint pathpoint5 = this.pathOptions[j]; - float f1 = pathpoint4.totalPathDistance + this.getTraversalCost(pathpoint4, pathpoint5); + float f1 = pathpoint4.totalPathDistance + this.getTraversalCost(pathpoint4, pathpoint5) + + this.getDirectionalPenalty(pathpoint4, pathpoint5, p_75861_3_); if (!pathpoint5.isAssigned() || f1 < pathpoint5.totalPathDistance) { pathpoint5.previous = pathpoint4; @@ -211,6 +212,24 @@ public class FlyPathFinder extends PathFinder { return DIAGONAL_3D_COST; } + private float getDirectionalPenalty(NPCPathPoint from, NPCPathPoint to, NPCPathPoint goal) { + int stepX = to.xCoord - from.xCoord; + int stepY = to.yCoord - from.yCoord; + int stepZ = to.zCoord - from.zCoord; + int goalX = goal.xCoord - from.xCoord; + int goalY = goal.yCoord - from.yCoord; + int goalZ = goal.zCoord - from.zCoord; + int stepLen = Math.abs(stepX) + Math.abs(stepY) + Math.abs(stepZ); + int goalLen = Math.abs(goalX) + Math.abs(goalY) + Math.abs(goalZ); + + if (stepLen == 0 || goalLen == 0) { + return 0.0F; + } + + float alignment = (float) (stepX * goalX + stepY * goalY + stepZ * goalZ) / (float) (stepLen * goalLen); + return (1.0F - alignment) * 0.04F; + } + private float estimateCostToTarget(NPCPathPoint from, NPCPathPoint to) { float dx = Math.abs(to.xCoord - from.xCoord); float dy = Math.abs(to.yCoord - from.yCoord); diff --git a/src/main/java/noppes/npcs/ai/pathfinder/PathNavigateFlying.java b/src/main/java/noppes/npcs/ai/pathfinder/PathNavigateFlying.java index df2e4c868..90856dc79 100644 --- a/src/main/java/noppes/npcs/ai/pathfinder/PathNavigateFlying.java +++ b/src/main/java/noppes/npcs/ai/pathfinder/PathNavigateFlying.java @@ -21,6 +21,10 @@ import noppes.npcs.entity.EntityNPCInterface; import javax.annotation.Nullable; public class PathNavigateFlying extends PathNavigate { + private static final long MIN_REPATH_INTERVAL_TICKS = 8L; + private static final long MAX_REPATH_INTERVAL_TICKS = 30L; + private static final int PATH_REUSE_TARGET_DELTA_SQ = 2; + private static final int FINAL_NODE_REUSE_DELTA_SQ = 3; private EntityNPCInterface theEntity; private World worldObj; @@ -83,16 +87,21 @@ public class PathNavigateFlying extends PathNavigate { } public void updatePath() { - if (this.worldObj.getTotalWorldTime() - this.lastTimeUpdated > 20L) { - if (this.targetPos != null) { - this.currentPath = null; - this.currentPath = this.getPathToXYZ(this.targetPos.getX(), this.targetPos.getY(), this.targetPos.getZ()); - this.lastTimeUpdated = this.worldObj.getTotalWorldTime(); - this.tryUpdatePath = false; - } - } else { - this.tryUpdatePath = true; + if (this.targetPos == null) { + this.tryUpdatePath = false; + return; } + + long now = this.worldObj.getTotalWorldTime(); + if (now - this.lastTimeUpdated < this.getAdaptiveRepathInterval()) { + this.tryUpdatePath = true; + return; + } + + this.currentPath = null; + this.currentPath = this.getPathToXYZ(this.targetPos.getX(), this.targetPos.getY(), this.targetPos.getZ()); + this.lastTimeUpdated = now; + this.tryUpdatePath = false; } public void setAvoidsWater(boolean p_75491_1_) { @@ -156,7 +165,8 @@ public class PathNavigateFlying extends PathNavigate { BlockPos pos = new BlockPos(x, y, z); if (!this.canNavigate()) { return null; - } else if (this.currentPath != null && !this.currentPath.isFinished() && pos.equals(this.targetPos)) { + } else if (this.canReuseCurrentPath(pos)) { + this.targetPos = pos; return this.currentPath; } else { this.targetPos = pos; @@ -180,7 +190,8 @@ public class PathNavigateFlying extends PathNavigate { return null; } else { BlockPos blockpos = new BlockPos(entity); - if (this.currentPath != null && !this.currentPath.isFinished() && blockpos.equals(this.targetPos)) { + if (this.canReuseCurrentPath(blockpos)) { + this.targetPos = blockpos; return this.currentPath; } else { this.targetPos = blockpos; @@ -215,6 +226,8 @@ public class PathNavigateFlying extends PathNavigate { this.removeSunnyPath(); } + this.smoothCurrentPath(); + if (this.currentPath.getCurrentPathLength() == 0) { return false; } else { @@ -242,6 +255,72 @@ public class PathNavigateFlying extends PathNavigate { return !this.isInLiquid() || (this.canSwim && this.isInLiquid()); } + private long getAdaptiveRepathInterval() { + if (this.targetPos == null) { + return MAX_REPATH_INTERVAL_TICKS; + } + double distance = this.theEntity.getDistance(this.targetPos.getX() + 0.5D, this.targetPos.getY(), this.targetPos.getZ() + 0.5D); + return (long) MathHelper.clamp_double(distance * 0.6D, MIN_REPATH_INTERVAL_TICKS, MAX_REPATH_INTERVAL_TICKS); + } + + private boolean canReuseCurrentPath(BlockPos newTarget) { + if (this.currentPath == null || this.currentPath.isFinished() || this.targetPos == null) { + return false; + } + + if (this.getBlockDistanceSq(this.targetPos, newTarget) <= PATH_REUSE_TARGET_DELTA_SQ) { + return true; + } + + NPCPathPoint finalPoint = this.currentPath.getFinalPathPoint(); + return finalPoint != null && this.getPointDistanceSq(finalPoint, newTarget) <= FINAL_NODE_REUSE_DELTA_SQ; + } + + private int getBlockDistanceSq(BlockPos a, BlockPos b) { + int dx = a.getX() - b.getX(); + int dy = a.getY() - b.getY(); + int dz = a.getZ() - b.getZ(); + return dx * dx + dy * dy + dz * dz; + } + + private int getPointDistanceSq(NPCPathPoint point, BlockPos blockPos) { + int dx = point.xCoord - blockPos.getX(); + int dy = point.yCoord - blockPos.getY(); + int dz = point.zCoord - blockPos.getZ(); + return dx * dx + dy * dy + dz * dz; + } + + private void smoothCurrentPath() { + if (this.currentPath == null) { + return; + } + int pathLength = this.currentPath.getCurrentPathLength(); + if (pathLength < 3) { + return; + } + + int width = MathHelper.ceiling_double_int(this.theEntity.width); + int height = MathHelper.ceiling_double_int(this.theEntity.height + 1.0F); + NPCPathPoint[] points = this.currentPath.points; + int writeIndex = 1; + int anchorIndex = 0; + + for (int readIndex = 2; readIndex < pathLength; readIndex++) { + Vec3 anchor = this.currentPath.getVectorFromIndex(this.theEntity, anchorIndex); + Vec3 probe = this.currentPath.getVectorFromIndex(this.theEntity, readIndex); + if (!this.isDirectPathBetweenPoints(anchor, probe, width, height)) { + points[writeIndex++] = points[readIndex - 1]; + anchorIndex = readIndex - 1; + } + } + + points[writeIndex++] = points[pathLength - 1]; + this.currentPath.setCurrentPathLength(writeIndex); + if (this.currentPath.getCurrentPathIndex() >= writeIndex) { + this.currentPath.setCurrentPathIndex(Math.max(0, writeIndex - 1)); + } + } + public void onUpdateNavigation() { ++this.totalTicks; diff --git a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java index c41e0e843..6e5923e7d 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java @@ -91,11 +91,13 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { this.prevLimbSwingAmount = this.limbSwingAmount; double distanceX = this.posX - this.prevPosX; double distanceZ = this.posZ - this.prevPosZ; - float distance = MathHelper.sqrt_double(distanceX * distanceX + distanceZ * distanceZ) * 4.0F; + float distance = MathHelper.sqrt_double(distanceX * distanceX + distanceZ * distanceZ); + float swingScale = this.getNavigator().noPath() ? 0.35F : 0.9F; + distance *= swingScale; if (distance > 1.0F) { distance = 1.0F; } - this.limbSwingAmount += (distance - this.limbSwingAmount) * 0.4F; + this.limbSwingAmount += (distance - this.limbSwingAmount) * 0.18F; this.limbSwing += this.limbSwingAmount; }