Improved and randomized pathfinder

Pathfinder now works better
--It levels of at the correct y level for pathfinding
--It pathfinder no longer in a perfect line but is randomized
--Pathfinder is now limited to search with a set range preventing cross
map interaction
This commit is contained in:
Robert Seifert 2013-05-23 02:56:49 -04:00
parent 05a4f5b418
commit 13699c24b3
3 changed files with 275 additions and 83 deletions

View file

@ -23,6 +23,7 @@ import net.minecraftforge.liquids.ILiquidTank;
import net.minecraftforge.liquids.ITankContainer;
import net.minecraftforge.liquids.LiquidContainerRegistry;
import net.minecraftforge.liquids.LiquidStack;
import universalelectricity.core.vector.Vector2;
import universalelectricity.core.vector.Vector3;
import universalelectricity.core.vector.VectorHelper;
@ -37,7 +38,16 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
private List<Vector3> targetSources = new ArrayList<Vector3>();
private List<Vector3> updateQue = new ArrayList<Vector3>();
private LiquidPathFinder pathFinder;
private LiquidPathFinder pathLiquid;
public LiquidPathFinder getLiquidFinder()
{
if(pathLiquid == null)
{
pathLiquid = new LiquidPathFinder(this.worldObj, false, 1000, 100);
}
return pathLiquid;
}
@Override
public String getMeterReading(EntityPlayer user, ForgeDirection side)
@ -49,7 +59,6 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
public void invalidate()
{
super.invalidate();
pathFinder = new LiquidPathFinder(this.worldObj, false, TileEntityDrain.MAX_WORLD_EDITS_PER_PROCESS * 2);
}
public boolean canDrainSources()
@ -99,13 +108,17 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
{
break;
}
if (request.getKey() instanceof ITankContainer)
{
ITankContainer tank = (ITankContainer) request.getKey();
Iterator<Vector3> it = this.targetSources.iterator();
while (it.hasNext())
Vector3[] sortedList = this.sortedDrainList();
for (int i = 0; sortedList != null && i < sortedList.length; i++)
{
Vector3 loc = it.next();
Vector3 loc = sortedList[i];
if (this.currentWorldEdits >= MAX_WORLD_EDITS_PER_PROCESS)
{
break;
@ -142,8 +155,6 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
/* REMOVE BLOCK */
loc.setBlock(this.worldObj, 0, 0, 2);
this.currentWorldEdits++;
it.remove();
}
}
}
@ -159,15 +170,12 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
*/
public void getNextFluidBlock()
{
if (pathFinder == null)
{
pathFinder = new LiquidPathFinder(this.worldObj, false, 1000);
}
pathFinder.reset();
pathFinder.init(new Vector3(this.xCoord + this.getFacing().offsetX, this.yCoord + this.getFacing().offsetY, this.zCoord + this.getFacing().offsetZ));
getLiquidFinder().reset();
getLiquidFinder().init(new Vector3(this.xCoord + this.getFacing().offsetX, this.yCoord + this.getFacing().offsetY, this.zCoord + this.getFacing().offsetZ));
// System.out.println("Nodes:" + pathFinder.nodes.size() + "Results:" +
// pathFinder.results.size());
for (Vector3 vec : pathFinder.nodes)
for (Vector3 vec : getLiquidFinder().nodes)
{
this.addVectorToQue(vec);
}
@ -211,6 +219,13 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
requests.remove();
}
}
}
public Vector3[] sortedDrainList()
{
try
{
/* CLEANUP TARGET LIST AND REMOVE INVALID SOURCES */
Iterator<Vector3> targetIt = this.targetSources.iterator();
while (targetIt.hasNext())
@ -221,63 +236,78 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
targetIt.remove();
}
}
Vector3[] sortedList = new Vector3[this.targetSources.size()];
for (int b = 0; b < this.targetSources.size(); b++)
{
sortedList[b] = this.targetSources.get(b);
}
/* SORT RESULTS TO PUT THE HiGHEST AND FURTHEST AT THE TOP */
try
Vector2 machine = new Vector3(this).toVector2();
for (int i = 0; i < sortedList.length; i++)
{
List<Vector3> updatedList = new ArrayList<Vector3>();
updatedList.addAll(this.targetSources);
for (int i = 0; i < updatedList.size(); i++)
Vector3 vec = sortedList[i].clone();
Vector2 first = vec.toVector2();
if (i + 1 < sortedList.length)
{
Vector3 vec = updatedList.get(i).clone();
if (i + 1 < updatedList.size())
Vector3 highest = vec;
int b = 0;
for (b = i + 1; b < sortedList.length; b++)
{
for (int b = i + 1; b < updatedList.size(); b++)
{
Vector3 checkVec = updatedList.get(b).clone();
Vector3 checkVec = sortedList[b].clone();
if (checkVec != null)
{
if (checkVec.distanceTo(new Vector3(this)) > vec.distanceTo(new Vector3(this)))
Vector2 second = checkVec.toVector2();
if (second.distanceTo(machine) > vec.toVector2().distanceTo(machine))
{
updatedList.set(i, checkVec);
updatedList.set(b, vec);
break;
highest = checkVec.clone();
}
}
}
}
}
for (int i = 0; i < updatedList.size(); i++)
if (b < sortedList.length)
{
Vector3 vec = updatedList.get(i).clone();
if (i + 1 < updatedList.size())
sortedList[i] = vec;
sortedList[b] = highest;
}
}
}
for (int i = 0; i < sortedList.length; i++)
{
for (int b = i + 1; b < updatedList.size(); b++)
Vector3 vec = sortedList[i].clone();
if (i + 1 < sortedList.length)
{
Vector3 checkVec = updatedList.get(b).clone();
Vector3 highest = vec;
int b = 0;
for (b = i + 1; b < sortedList.length; b++)
{
Vector3 checkVec = sortedList[b].clone();
if (checkVec != null)
{
if (checkVec.intY() > vec.intY())
if (checkVec.intY() > highest.intY())
{
updatedList.set(i, checkVec);
updatedList.set(b, vec);
break;
}
}
}
}
}
this.targetSources.clear();
this.targetSources.addAll(updatedList);
updatedList.clear();
highest = checkVec.clone();
}
}
}
if (b < sortedList.length)
{
sortedList[i] = vec;
sortedList[b] = highest;
}
}
}
return sortedList;
}
catch (Exception e)
{
System.out.println("FluidMech: Error sorting fill collection \n");
System.out.println("FluidMech: Critical Error Processing Drain List");
e.printStackTrace();
}
return null;
}
@Override
@ -319,16 +349,15 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
int blocks = (resource.amount / LiquidContainerRegistry.BUCKET_VOLUME);
/* FIND ALL VALID BLOCKS ON LEVEL OR BELLOW */
LiquidPathFinder pathFinder = new LiquidPathFinder(this.worldObj, true, this.MAX_WORLD_EDITS_PER_PROCESS * 2);
final Vector3 faceVec = new Vector3(this.xCoord + this.getFacing().offsetX, this.yCoord + this.getFacing().offsetY, this.zCoord + this.getFacing().offsetZ);
pathFinder.init(faceVec);
getLiquidFinder().init(faceVec);
/* SORT RESULTS TO PUT THE LOWEST AND CLOSEST AT THE TOP */
try
{
if (pathFinder.results.size() > 1)
if (getLiquidFinder().results.size() > 1)
{
Collections.sort(pathFinder.results, new Comparator()
Collections.sort(getLiquidFinder().results, new Comparator()
{
@Override
public int compare(Object o1, Object o2)
@ -362,7 +391,7 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
}
/* START FILLING IN OR CHECKING IF CAN FILL AREA */
int fillable = 0;
for (Vector3 loc : pathFinder.results)
for (Vector3 loc : getLiquidFinder().results)
{
if (blocks <= 0)
{
@ -387,7 +416,7 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta
}
for (Vector3 loc : pathFinder.results)
for (Vector3 loc : getLiquidFinder().results)
{
if (blocks <= 0)
{

View file

@ -3,11 +3,14 @@ package fluidmech.common.pump.path;
import hydraulic.helpers.FluidHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.ForgeDirection;
import universalelectricity.core.vector.Vector2;
import universalelectricity.core.vector.Vector3;
/**
@ -24,9 +27,14 @@ public class LiquidPathFinder
private boolean fill; /* ARE WE FILLING THE PATH OR DRAINING THE PATH */
private ForgeDirection priority; /* BASED ON fill -- WHICH DIRECTION WILL THE PATH GO FIRST */
private int resultLimit = 2000;
private Vector2 Start;
private double range;
private Random random = new Random();
List<ForgeDirection> bn = new ArrayList<ForgeDirection>();
public LiquidPathFinder(final World world, final boolean fill, final int resultLimit)
public LiquidPathFinder(final World world, final boolean fill, final int resultLimit, final double range)
{
this.range = range;
this.world = world;
this.fill = fill;
if (fill)
@ -39,6 +47,10 @@ public class LiquidPathFinder
}
this.resultLimit = resultLimit;
this.reset();
bn.add(ForgeDirection.EAST);
bn.add(ForgeDirection.WEST);
bn.add(ForgeDirection.NORTH);
bn.add(ForgeDirection.SOUTH);
}
/**
@ -73,30 +85,27 @@ public class LiquidPathFinder
return false;
}
vec = node.clone().modifyPositionFromSide(this.priority);
if (this.isValidNode(vec) & !this.nodes.contains(vec))
if (find(this.priority, node.clone()))
{
if (this.findNodes(vec))
return true;
}
Collections.shuffle(bn);
Collections.shuffle(bn);
for (ForgeDirection direction : bn)
{
if (find(direction, vec))
{
return true;
}
}
for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS)
{
if (direction != this.priority)
{
vec = node.clone().modifyPositionFromSide(direction);
if (this.isValidNode(vec) & !this.nodes.contains(vec))
{
if (this.findNodes(vec))
if (find(this.priority.getOpposite(), node.clone()))
{
return true;
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
@ -105,6 +114,20 @@ public class LiquidPathFinder
return false;
}
public boolean find(ForgeDirection direction, Vector3 vec)
{
vec = vec.clone().modifyPositionFromSide(direction);
double distance = vec.toVector2().distanceTo(this.Start);
if (distance <= this.range && this.isValidNode(vec) & !this.nodes.contains(vec))
{
if (this.findNodes(vec))
{
return true;
}
}
return false;
}
public boolean isValidNode(Vector3 pos)
{
int blockID = pos.getBlockID(world);
@ -130,8 +153,9 @@ public class LiquidPathFinder
/**
* Called to execute the pathfinding operation.
*/
public LiquidPathFinder init(Vector3 startNode)
public LiquidPathFinder init(final Vector3 startNode)
{
this.Start = startNode.toVector2();
this.findNodes(startNode);
if (this.fill && this.isValidNode(startNode))
{

View file

@ -0,0 +1,139 @@
package fluidmech.common.pump.path;
import hydraulic.helpers.FluidHelper;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.ForgeDirection;
import universalelectricity.core.vector.Vector3;
/**
* A simpler pathfinder based on Calclavia's PathFinder from UE api
*/
public class LiquidPathFinder2D
{
private World world; /* MC WORLD */
public List<Vector3> nodes = new ArrayList<Vector3>(); /*
* LOCATIONs THE PATH FINDER HAS GONE
* OVER
*/
public List<Vector3> results = new ArrayList<Vector3>();/* LOCATIONS THAT ARE VALID RESULTS */
private boolean fill; /* ARE WE FILLING THE PATH OR DRAINING THE PATH */
private ForgeDirection priority; /* BASED ON fill -- WHICH DIRECTION WILL THE PATH GO FIRST */
private int resultLimit = 2000;
public LiquidPathFinder2D(final World world, final int resultLimit)
{
this.world = world;
this.fill = fill;
if (fill)
{
priority = ForgeDirection.DOWN;
}
else
{
priority = ForgeDirection.UP;
}
this.resultLimit = resultLimit;
this.reset();
}
/**
* @return True on success finding, false on failure.
*/
public boolean findNodes(final Vector3 node)
{
try
{
Vector3 vec = node.clone();
this.nodes.add(node);
Chunk chunk = this.world.getChunkFromBlockCoords(vec.intX(), vec.intZ());
if (chunk == null || !chunk.isChunkLoaded)
{
return true;
}
int id = node.getBlockID(world);
int meta = node.getBlockID(world);
if (this.fill && (id == 0 || (FluidHelper.getLiquidFromBlockId(id) != null && meta != 0)))
{
this.results.add(node);
}
else if (!this.fill && FluidHelper.isSourceBlock(world, node))
{
this.results.add(node);
}
if (this.isDone(node))
{
return false;
}
for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS)
{
if (direction != ForgeDirection.DOWN && direction != ForgeDirection.UP)
{
vec = node.clone().modifyPositionFromSide(direction);
if (this.isValidNode(vec) & !this.nodes.contains(vec))
{
if (this.findNodes(vec))
{
return true;
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
public boolean isValidNode(Vector3 pos)
{
int blockID = pos.getBlockID(world);
if (!this.fill)
{
return FluidHelper.getLiquidFromBlockId(pos.getBlockID(world)) != null;
}
else
{
return FluidHelper.getLiquidFromBlockId(pos.getBlockID(world)) != null || (blockID == 0 && FluidHelper.getConnectedSources(world, pos) > 0);
}
}
public boolean isDone(Vector3 vec)
{
if (this.results.size() >= this.resultLimit || this.nodes.size() >= 4000)
{
return true;
}
return false;
}
/**
* Called to execute the pathfinding operation.
*/
public LiquidPathFinder2D init(Vector3 startNode)
{
this.findNodes(startNode);
if (this.fill && this.isValidNode(startNode))
{
}
return this;
}
public LiquidPathFinder2D reset()
{
this.nodes.clear();
this.results.clear();
return this;
}
}