package electroblob.wizardry.misc; import com.google.common.collect.Sets; import electroblob.wizardry.spell.Clairvoyance; import net.minecraft.entity.EntityLiving; import net.minecraft.pathfinding.NodeProcessor; import net.minecraft.pathfinding.Path; import net.minecraft.pathfinding.PathHeap; import net.minecraft.pathfinding.PathPoint; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import javax.annotation.Nullable; import java.util.Set; /** * Minecraft's pathfinder refused to play nicely, so I 'borrowed' its code and fiddled with it. Currently this is only * used for the {@link Clairvoyance} spell. */ public class WizardryPathFinder { /** The path being generated */ private final PathHeap path = new PathHeap(); private final Set closedSet = Sets.newHashSet(); /** Selection of path points to add to the path */ private final PathPoint[] pathOptions = new PathPoint[32]; private final NodeProcessor nodeProcessor; public WizardryPathFinder(NodeProcessor processor){ this.nodeProcessor = processor; } @Nullable public Path findPath(IBlockAccess world, EntityLiving entity, BlockPos destination, float range){ return this.findPath(world, entity, (double)((float)destination.getX() + 0.5F), (double)((float)destination.getY() + 0.5F), (double)((float)destination.getZ() + 0.5F), range); } @Nullable private Path findPath(IBlockAccess world, EntityLiving entity, double x, double y, double z, float range){ this.path.clearPath(); this.nodeProcessor.init(world, entity); PathPoint pathpoint = this.nodeProcessor.getStart(); PathPoint pathpoint1 = this.nodeProcessor.getPathPointToCoords(x, y, z); Path path = this.findPath(pathpoint, pathpoint1, range); this.nodeProcessor.postProcess(); return path; } @Nullable private Path findPath(PathPoint start, PathPoint end, float range){ start.totalPathDistance = 0.0F; start.distanceToNext = start.distanceManhattan(end); start.distanceToTarget = start.distanceToNext; this.path.clearPath(); this.closedSet.clear(); this.path.addPoint(start); PathPoint pathpoint = start; int i = 0; while(!this.path.isPathEmpty()){ ++i; // This is the offending line - timeout limit changed from 200 to 5000. if(i >= 5000){ break; } PathPoint pathpoint1 = this.path.dequeue(); if(pathpoint1.equals(end)){ pathpoint = end; break; } if(pathpoint1.distanceManhattan(end) < pathpoint.distanceManhattan(end)){ pathpoint = pathpoint1; } pathpoint1.visited = true; int j = this.nodeProcessor.findPathOptions(this.pathOptions, pathpoint1, end, range); for(int k = 0; k < j; ++k){ PathPoint pathpoint2 = this.pathOptions[k]; float f = pathpoint1.distanceManhattan(pathpoint2); pathpoint2.distanceFromOrigin = pathpoint1.distanceFromOrigin + f; pathpoint2.cost = f + pathpoint2.costMalus; float f1 = pathpoint1.totalPathDistance + pathpoint2.cost; if(pathpoint2.distanceFromOrigin < range && (!pathpoint2.isAssigned() || f1 < pathpoint2.totalPathDistance)){ pathpoint2.previous = pathpoint1; pathpoint2.totalPathDistance = f1; pathpoint2.distanceToNext = pathpoint2.distanceManhattan(end) + pathpoint2.costMalus; if(pathpoint2.isAssigned()){ this.path.changeDistance(pathpoint2, pathpoint2.totalPathDistance + pathpoint2.distanceToNext); }else{ pathpoint2.distanceToTarget = pathpoint2.totalPathDistance + pathpoint2.distanceToNext; this.path.addPoint(pathpoint2); } } } } if(pathpoint == start){ return null; }else{ Path path = this.createEntityPath(start, pathpoint); return path; } } /** * Returns a new PathEntity for a given start and end point */ private Path createEntityPath(PathPoint start, PathPoint end){ int i = 1; for(PathPoint pathpoint = end; pathpoint.previous != null; pathpoint = pathpoint.previous){ ++i; } PathPoint[] points = new PathPoint[i]; PathPoint pathpoint1 = end; --i; for(points[i] = end; pathpoint1.previous != null; points[i] = pathpoint1){ pathpoint1 = pathpoint1.previous; --i; } return new Path(points); } }