package org.rsbot.script.wrappers;
import org.rsbot.script.methods.MethodContext;
import java.util.*;
/**
*/
public class RSLocalPath extends RSPath {
public static final int WALL_NORTH_WEST = 0x1;
public static final int WALL_NORTH = 0x2;
public static final int WALL_NORTH_EAST = 0x4;
public static final int WALL_EAST = 0x8;
public static final int WALL_SOUTH_EAST = 0x10;
public static final int WALL_SOUTH = 0x20;
public static final int WALL_SOUTH_WEST = 0x40;
public static final int WALL_WEST = 0x80;
public static final int BLOCKED = 0x100;
protected final RSTile end;
protected RSTile base;
protected int[][] flags;
protected int offX, offY;
private RSTilePath tilePath;
public RSLocalPath(final MethodContext ctx, final RSTile end) {
super(ctx);
this.end = end;
}
/**
* {@inheritDoc}
*/
@Override
public boolean traverse(final EnumSet<TraversalOption> options) {
return getNext() != null && tilePath.traverse(options);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValid() {
return getNext() != null && !methods.players.getMyPlayer().getLocation().equals(getEnd());
}
/**
* {@inheritDoc}
*/
@Override
public RSTile getNext() {
if (!methods.game.getMapBase().equals(base)) {
final int[][] flags = methods.walking.getCollisionFlags(methods.game.getPlane());
if (flags != null) {
base = methods.game.getMapBase();
final RSTile start = methods.players.getMyPlayer().getLocation();
final RSTile[] tiles = findPath(start, end);
if (tiles == null) {
base = null;
return null;
}
tilePath = methods.walking.newTilePath(tiles);
}
}
return tilePath.getNext();
}
/**
* {@inheritDoc}
*/
@Override
public RSTile getStart() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public RSTile getEnd() {
return end;
}
/**
* Returns the calculated RSTilePath that is currently
* providing data to this RSLocalPath.
*
* @return The current RSTile path; or <code>null</code>.
*/
public RSTilePath getCurrentTilePath() {
return tilePath;
}
protected class Node {
public final int x;
public final int y;
public Node prev;
public double g, f;
public boolean border;
public Node(final int x, final int y, final boolean border) {
this.border = border;
this.x = x;
this.y = y;
g = f = 0;
}
public Node(final int x, final int y) {
this.x = x;
this.y = y;
g = f = 0;
}
@Override
public int hashCode() {
return x << 4 | y;
}
@Override
public boolean equals(final Object o) {
if (o instanceof Node) {
final Node n = (Node) o;
return x == n.x && y == n.y;
}
return false;
}
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
public RSTile toRSTile(final int baseX, final int baseY) {
return new RSTile(x + baseX, y + baseY);
}
}
protected RSTile[] findPath(final RSTile start, final RSTile end) {
return findPath(start, end, false);
}
private RSTile[] findPath(final RSTile start, final RSTile end, boolean remote) {
final int base_x = base.getX(), base_y = base.getY();
final int curr_x = start.getX() - base_x, curr_y = start.getY() - base_y;
int dest_x = end.getX() - base_x, dest_y = end.getY() - base_y;
// load client data
final int plane = methods.game.getPlane();
flags = methods.walking.getCollisionFlags(plane);
final RSTile offset = methods.walking.getCollisionOffset(plane);
offX = offset.getX();
offY = offset.getY();
// loaded region only
if (flags == null || curr_x < 0 || curr_y < 0 || curr_x >= flags.length || curr_y >= flags.length) {
return null;
} else if (dest_x < 0 || dest_y < 0 || dest_x >= flags.length || dest_y >= flags.length) {
remote = true;
if (dest_x < 0) {
dest_x = 0;
} else if (dest_x >= flags.length) {
dest_x = flags.length - 1;
}
if (dest_y < 0) {
dest_y = 0;
} else if (dest_y >= flags.length) {
dest_y = flags.length - 1;
}
}
// structs
final HashSet<Node> open = new HashSet<Node>();
final HashSet<Node> closed = new HashSet<Node>();
Node curr = new Node(curr_x, curr_y);
final Node dest = new Node(dest_x, dest_y);
curr.f = heuristic(curr, dest);
open.add(curr);
// search
while (!open.isEmpty()) {
curr = lowest_f(open);
if (curr.equals(dest)) {
// reconstruct from pred tree
return path(curr, base_x, base_y);
}
open.remove(curr);
closed.add(curr);
for (final Node next : successors(curr)) {
if (!closed.contains(next)) {
final double t = curr.g + dist(curr, next);
boolean use_t = false;
if (!open.contains(next)) {
open.add(next);
use_t = true;
} else if (t < next.g) {
use_t = true;
}
if (use_t) {
next.prev = curr;
next.g = t;
next.f = t + heuristic(next, dest);
}
}
}
}
// no path
if (!remote || methods.calc.distanceTo(end) < 10) {
return null;
}
return findPath(start, pull(end));
}
private RSTile pull(final RSTile tile) {
final RSTile p = methods.players.getMyPlayer().getLocation();
int x = tile.getX(), y = tile.getY();
if (p.getX() < x) {
x -= 2;
} else if (p.getX() > x) {
x += 2;
}
if (p.getY() < y) {
y -= 2;
} else if (p.getY() > y) {
y += 2;
}
return new RSTile(x, y);
}
private double heuristic(final Node start, final Node end) {
double dx = start.x - end.x;
double dy = start.y - end.y;
if (dx < 0) {
dx = -dx;
}
if (dy < 0) {
dy = -dy;
}
return dx < dy ? dy : dx;
//double diagonal = dx > dy ? dy : dx;
//double manhattan = dx + dy;
//return 1.41421356 * diagonal + (manhattan - 2 * diagonal);
}
private double dist(final Node start, final Node end) {
if (start.x != end.x && start.y != end.y) {
return 1.41421356;
} else {
return 1.0;
}
}
private Node lowest_f(final Set<Node> open) {
Node best = null;
for (final Node t : open) {
if (best == null || t.f < best.f) {
best = t;
}
}
return best;
}
private RSTile[] path(final Node end, final int base_x, final int base_y) {
final LinkedList<RSTile> path = new LinkedList<RSTile>();
Node p = end;
while (p != null) {
path.addFirst(p.toRSTile(base_x, base_y));
p = p.prev;
}
return path.toArray(new RSTile[path.size()]);
}
private List<Node> successors(final Node t) {
final LinkedList<Node> tiles = new LinkedList<Node>();
final int x = t.x, y = t.y;
final int f_x = x - offX, f_y = y - offY;
final int here = flags[f_x][f_y];
final int upper = flags.length - 1;
if (f_y > 0 && (here & WALL_SOUTH) == 0 && (flags[f_x][f_y - 1] & BLOCKED) == 0) {
tiles.add(new Node(x, y - 1));
}
if (f_x > 0 && (here & WALL_WEST) == 0 && (flags[f_x - 1][f_y] & BLOCKED) == 0) {
tiles.add(new Node(x - 1, y));
}
if (f_y < upper && (here & WALL_NORTH) == 0 && (flags[f_x][f_y + 1] & BLOCKED) == 0) {
tiles.add(new Node(x, y + 1));
}
if (f_x < upper && (here & WALL_EAST) == 0 && (flags[f_x + 1][f_y] & BLOCKED) == 0) {
tiles.add(new Node(x + 1, y));
}
if (f_x > 0 && f_y > 0 && (here & (WALL_SOUTH_WEST | WALL_SOUTH | WALL_WEST)) == 0
&& (flags[f_x - 1][f_y - 1] & BLOCKED) == 0
&& (flags[f_x][f_y - 1] & (BLOCKED | WALL_WEST)) == 0
&& (flags[f_x - 1][f_y] & (BLOCKED | WALL_SOUTH)) == 0) {
tiles.add(new Node(x - 1, y - 1));
}
if (f_x > 0 && f_y < upper && (here & (WALL_NORTH_WEST | WALL_NORTH | WALL_WEST)) == 0
&& (flags[f_x - 1][f_y + 1] & BLOCKED) == 0
&& (flags[f_x][f_y + 1] & (BLOCKED | WALL_WEST)) == 0
&& (flags[f_x - 1][f_y] & (BLOCKED | WALL_NORTH)) == 0) {
tiles.add(new Node(x - 1, y + 1));
}
if (f_x < upper && f_y > 0 && (here & (WALL_SOUTH_EAST | WALL_SOUTH | WALL_EAST)) == 0
&& (flags[f_x + 1][f_y - 1] & BLOCKED) == 0
&& (flags[f_x][f_y - 1] & (BLOCKED | WALL_EAST)) == 0
&& (flags[f_x + 1][f_y] & (BLOCKED | WALL_SOUTH)) == 0) {
tiles.add(new Node(x + 1, y - 1));
}
if (f_x > 0 && f_y < upper && (here & (WALL_NORTH_EAST | WALL_NORTH | WALL_EAST)) == 0
&& (flags[f_x + 1][f_y + 1] & BLOCKED) == 0
&& (flags[f_x][f_y + 1] & (BLOCKED | WALL_EAST)) == 0
&& (flags[f_x + 1][f_y] & (BLOCKED | WALL_NORTH)) == 0) {
tiles.add(new Node(x + 1, y + 1));
}
return tiles;
}
}