Add pathfinding API

This commit is contained in:
GeChallengeM
2021-08-01 18:15:15 +02:00
parent c726d477a8
commit 38939ac971
3 changed files with 347 additions and 0 deletions

View File

@@ -236,6 +236,258 @@ public abstract class RSTileMixin implements RSTile
return true;
}
@Inject
@Override
public List<Tile> pathTo(Tile other)
{
int z = this.getPlane();
if (z != other.getPlane())
{
return null;
}
CollisionData[] collisionData = client.getCollisionMaps();
if (collisionData == null)
{
return null;
}
int[][] directions = new int[128][128];
int[][] distances = new int[128][128];
int[] bufferX = new int[4096];
int[] bufferY = new int[4096];
// Initialise directions and distances
for (int i = 0; i < 128; ++i)
{
for (int j = 0; j < 128; ++j)
{
directions[i][j] = 0;
distances[i][j] = Integer.MAX_VALUE;
}
}
Point p1 = this.getSceneLocation();
Point p2 = other.getSceneLocation();
int middleX = p1.getX();
int middleY = p1.getY();
int currentX = middleX;
int currentY = middleY;
int offsetX = 64;
int offsetY = 64;
// Initialise directions and distances for starting tile
directions[offsetX][offsetY] = 99;
distances[offsetX][offsetY] = 0;
int index1 = 0;
bufferX[0] = currentX;
int index2 = 1;
bufferY[0] = currentY;
int[][] collisionDataFlags = collisionData[z].getFlags();
boolean isReachable = false;
while (index1 != index2)
{
currentX = bufferX[index1];
currentY = bufferY[index1];
index1 = index1 + 1 & 4095;
// currentX is for the local coordinate while currentMapX is for the index in the directions and distances arrays
int currentMapX = currentX - middleX + offsetX;
int currentMapY = currentY - middleY + offsetY;
if ((currentX == p2.getX()) && (currentY == p2.getY()))
{
isReachable = true;
break;
}
int currentDistance = distances[currentMapX][currentMapY] + 1;
if (currentMapX > 0 && directions[currentMapX - 1][currentMapY] == 0 && (collisionDataFlags[currentX - 1][currentY] & 19136776) == 0)
{
// Able to move 1 tile west
bufferX[index2] = currentX - 1;
bufferY[index2] = currentY;
index2 = index2 + 1 & 4095;
directions[currentMapX - 1][currentMapY] = 2;
distances[currentMapX - 1][currentMapY] = currentDistance;
}
if (currentMapX < 127 && directions[currentMapX + 1][currentMapY] == 0 && (collisionDataFlags[currentX + 1][currentY] & 19136896) == 0)
{
// Able to move 1 tile east
bufferX[index2] = currentX + 1;
bufferY[index2] = currentY;
index2 = index2 + 1 & 4095;
directions[currentMapX + 1][currentMapY] = 8;
distances[currentMapX + 1][currentMapY] = currentDistance;
}
if (currentMapY > 0 && directions[currentMapX][currentMapY - 1] == 0 && (collisionDataFlags[currentX][currentY - 1] & 19136770) == 0)
{
// Able to move 1 tile south
bufferX[index2] = currentX;
bufferY[index2] = currentY - 1;
index2 = index2 + 1 & 4095;
directions[currentMapX][currentMapY - 1] = 1;
distances[currentMapX][currentMapY - 1] = currentDistance;
}
if (currentMapY < 127 && directions[currentMapX][currentMapY + 1] == 0 && (collisionDataFlags[currentX][currentY + 1] & 19136800) == 0)
{
// Able to move 1 tile north
bufferX[index2] = currentX;
bufferY[index2] = currentY + 1;
index2 = index2 + 1 & 4095;
directions[currentMapX][currentMapY + 1] = 4;
distances[currentMapX][currentMapY + 1] = currentDistance;
}
if (currentMapX > 0 && currentMapY > 0 && directions[currentMapX - 1][currentMapY - 1] == 0 && (collisionDataFlags[currentX - 1][currentY - 1] & 19136782) == 0 && (collisionDataFlags[currentX - 1][currentY] & 19136776) == 0 && (collisionDataFlags[currentX][currentY - 1] & 19136770) == 0)
{
// Able to move 1 tile south-west
bufferX[index2] = currentX - 1;
bufferY[index2] = currentY - 1;
index2 = index2 + 1 & 4095;
directions[currentMapX - 1][currentMapY - 1] = 3;
distances[currentMapX - 1][currentMapY - 1] = currentDistance;
}
if (currentMapX < 127 && currentMapY > 0 && directions[currentMapX + 1][currentMapY - 1] == 0 && (collisionDataFlags[currentX + 1][currentY - 1] & 19136899) == 0 && (collisionDataFlags[currentX + 1][currentY] & 19136896) == 0 && (collisionDataFlags[currentX][currentY - 1] & 19136770) == 0)
{
// Able to move 1 tile north-west
bufferX[index2] = currentX + 1;
bufferY[index2] = currentY - 1;
index2 = index2 + 1 & 4095;
directions[currentMapX + 1][currentMapY - 1] = 9;
distances[currentMapX + 1][currentMapY - 1] = currentDistance;
}
if (currentMapX > 0 && currentMapY < 127 && directions[currentMapX - 1][currentMapY + 1] == 0 && (collisionDataFlags[currentX - 1][currentY + 1] & 19136824) == 0 && (collisionDataFlags[currentX - 1][currentY] & 19136776) == 0 && (collisionDataFlags[currentX][currentY + 1] & 19136800) == 0)
{
// Able to move 1 tile south-east
bufferX[index2] = currentX - 1;
bufferY[index2] = currentY + 1;
index2 = index2 + 1 & 4095;
directions[currentMapX - 1][currentMapY + 1] = 6;
distances[currentMapX - 1][currentMapY + 1] = currentDistance;
}
if (currentMapX < 127 && currentMapY < 127 && directions[currentMapX + 1][currentMapY + 1] == 0 && (collisionDataFlags[currentX + 1][currentY + 1] & 19136992) == 0 && (collisionDataFlags[currentX + 1][currentY] & 19136896) == 0 && (collisionDataFlags[currentX][currentY + 1] & 19136800) == 0)
{
// Able to move 1 tile north-east
bufferX[index2] = currentX + 1;
bufferY[index2] = currentY + 1;
index2 = index2 + 1 & 4095;
directions[currentMapX + 1][currentMapY + 1] = 12;
distances[currentMapX + 1][currentMapY + 1] = currentDistance;
}
}
if (!isReachable)
{
// Try find a different reachable tile in the 21x21 area around the target tile, as close as possible to the target tile
int upperboundDistance = Integer.MAX_VALUE;
int pathLength = Integer.MAX_VALUE;
int checkRange = 10;
int approxDestinationX = p2.getX();
int approxDestinationY = p2.getY();
for (int i = approxDestinationX - checkRange; i <= checkRange + approxDestinationX; ++i)
{
for (int j = approxDestinationY - checkRange; j <= checkRange + approxDestinationY; ++j)
{
int currentMapX = i - middleX + offsetX;
int currentMapY = j - middleY + offsetY;
if (currentMapX >= 0 && currentMapY >= 0 && currentMapX < 128 && currentMapY < 128 && distances[currentMapX][currentMapY] < 100)
{
int deltaX = 0;
if (i < approxDestinationX)
{
deltaX = approxDestinationX - i;
}
else if (i > approxDestinationX)
{
deltaX = i - (approxDestinationX);
}
int deltaY = 0;
if (j < approxDestinationY)
{
deltaY = approxDestinationY - j;
}
else if (j > approxDestinationY)
{
deltaY = j - (approxDestinationY);
}
int distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < upperboundDistance || distanceSquared == upperboundDistance && distances[currentMapX][currentMapY] < pathLength)
{
upperboundDistance = distanceSquared;
pathLength = distances[currentMapX][currentMapY];
currentX = i;
currentY = j;
}
}
}
}
if (upperboundDistance == Integer.MAX_VALUE)
{
// No path found
return null;
}
}
// Getting path from directions and distances
bufferX[0] = currentX;
bufferY[0] = currentY;
int index = 1;
int directionNew;
int directionOld;
for (directionNew = directionOld = directions[currentX - middleX + offsetX][currentY - middleY + offsetY]; p1.getX() != currentX || p1.getY() != currentY; directionNew = directions[currentX - middleX + offsetX][currentY - middleY + offsetY])
{
if (directionNew != directionOld)
{
// "Corner" of the path --> new checkpoint tile
directionOld = directionNew;
bufferX[index] = currentX;
bufferY[index++] = currentY;
}
if ((directionNew & 2) != 0)
{
++currentX;
}
else if ((directionNew & 8) != 0)
{
--currentX;
}
if ((directionNew & 1) != 0)
{
++currentY;
}
else if ((directionNew & 4) != 0)
{
--currentY;
}
}
int checkpointTileNumber = 1;
Tile[][][] tiles = client.getScene().getTiles();
List<Tile> checkpointTiles = new ArrayList<>();
while (index-- > 0)
{
checkpointTiles.add(tiles[this.getPlane()][bufferX[index]][bufferY[index]]);
if (checkpointTileNumber == 25)
{
// Pathfinding only supports up to the 25 first checkpoint tiles
break;
}
checkpointTileNumber++;
}
return checkpointTiles;
}
@Inject
@Override
public List<TileItem> getGroundItems()