package net.scapeemulator.game.model.mob;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
import net.scapeemulator.game.model.Position;
/**
* This {@link WalkingQueue} is associated with a certain {@link Mob}.
* This should handle the {@link Deque} for the walking of the associated
* {@link Mob}. There is a {@link Deque} to hold the positions to walk to (head
* to tail), as well as a {@link Deque} holding the most recent {@link Position}
* points walked to.
*
* @author ?
*/
public final class WalkingQueue {
/* Maximum amount of points in recentPoints. */
private final static int AMOUNT_POINTS = 100;
private final Mob mob;
private final Deque<Position> points = new ArrayDeque<>();
private boolean runningQueue = false;
private boolean minimapFlagReset = false;
private final Deque<Position> recentPoints = new LinkedList<>();
/**
* Create a {@link WalkingQueue} associated with a certain {@link Mob}.
*
* @param mob The {@link Mob} to associate this with.
*/
public WalkingQueue(Mob mob) {
this.mob = mob;
}
/**
* Reset the {@link WalkingQueue}. This will clear all points, set the
* mini-map Flag to be reset and {@link isRunningQueue()} to false.
*/
public void reset() {
points.clear();
runningQueue = false;
minimapFlagReset = true;
}
/**
* Add a position to the back of the {@link Deque}. This point may or may
* not be used to walk to, in the future.
*
* @param position The {@link Position}.
*/
public void addPoint(Position position) {
points.add(position);
}
/**
* Add the first step from {@link Mob#getPosition()} to position. This
* clears all current {@link Position} points on the {@link Deque}.
* {@link getRunningQueue()} will be set to false. Steps will be added as
* defined by {@code addStepImpl(position, mob.getPosition())}.
*
* @param position The {@link Position} to walk to.
* @see addStepImpl(Position, Position)
*/
public void addFirstStep(Position position) {
points.clear();
runningQueue = false;
addStepImpl(position, mob.getPosition());
}
/**
* Add steps from the last {@link Position} we walk to, to position. Adds
* steps to the {@link Deque} from the last element on the {@link Deque} to
* position.
*
* @param position The {@link Position} to walk to.
* @see addStepImpl(Position, Position)
*/
public void addStep(Position position) {
addStepImpl(position, points.peekLast());
}
/**
* Peek which {@link Position} is next, to walk to. Peeks which
* {@link Position} is first in the {@link Deque}.
*
* @return The first element on the {@link Deque}. Returns null when there
* is no first element.
*/
public Position peek() {
return points.peekFirst();
}
/**
* Add All {@link Position} points from last to position, to the
* {@link Deque}.
*
* @param position The {@link Position} to walk to.
* @param last The {@link Position} to walk from.
*/
private void addStepImpl(Position position, Position last) {
int deltaX = position.getX() - last.getX();
int deltaY = position.getY() - last.getY();
int max = Math.max(Math.abs(deltaX), Math.abs(deltaY));
/* Bug fix? */
if (max < 1) {
max = 1;
}
for (int i = 0; i < max; i++) {
if (deltaX < 0) {
deltaX++;
} else if (deltaX > 0) {
deltaX--;
}
if (deltaY < 0) {
deltaY++;
} else if (deltaY > 0) {
deltaY--;
}
points.add(new Position(position.getX() - deltaX, position.getY() - deltaY, position.getHeight()));
}
}
/**
* Perform the tick for the {@link WalkingQueue}. 1) The associated
* {@link Mob} will change position to the first {@link Position} A, on the
* {@link Deque}. The old point will be added to the "most recent" points.
* {@link Mob#getFirstDirection()} will be adjusted as well. 2) If
* {@link Mob#isRunning()} or {@link isRunningQueue()}, do the same for the
* next point and adjust {@link Mob#getSecondDirection()}. If direction to A
* is not traversable, performs a {@link reset()} instead of everything else
* described.
*/
public void tick() {
Position position = mob.getPosition();
Direction firstDirection = Direction.NONE;
Direction secondDirection = Direction.NONE;
Position next = points.poll();
if (next != null) {
Direction direction = Direction.between(position, next);
boolean traversable = !mob.isClipped() || Direction.isTraversable(position, direction, mob.getSize());
if (traversable) {
firstDirection = direction;
addRecentPoint(position);
position = next;
if (runningQueue || mob.isRunning()) {
next = points.poll();
if (next != null) {
direction = Direction.between(position, next);
traversable = !mob.isClipped() || Direction.isTraversable(position, direction, mob.getSize());
if (traversable) {
secondDirection = direction;
addRecentPoint(position);
position = next;
}
}
}
}
if (!traversable) {
reset();
}
}
mob.setDirections(firstDirection, secondDirection);
mob.setPosition(position);
}
/**
* Add position to the tail of {@link getRecentPoints()}. When
* {@link getRecentPoints()} has reached its maximum size,
* {@link AMOUNT_POINTS} the head will be removed.
*
* @param position The {@link Position} to add to the most recent points
* {@link Deque}.
*/
public void addRecentPoint(Position position) {
if (recentPoints.size() >= AMOUNT_POINTS) {
recentPoints.poll();
}
recentPoints.addLast(position);
}
/**
* Gets a {@link Deque} listing all recent {@link Position} points. The
* elements at the tail of this will be the most recent, head least.
*
* @return The {@link Deque} of last recent points.
*/
public Deque<Position> getRecentPoints() {
return recentPoints;
}
/**
* Gets whether we have no {@link Position} to walk to.
* @return Checks whether the {@link Deque} holding the "walk to" points is
* empty.
*/
public boolean isEmpty() {
return points.isEmpty();
}
/**
* Gets whether this {@link WalkingQueue} is "running".
* @return Whether it is "running".
*/
public boolean isRunningQueue() {
return runningQueue;
}
/**
* Set whether this {@link WalkingQueue} is "running". If it is, walking
* will be twice as fast = "running".
*
* @param runningQueue Whether it is "running".
* @see tick()
*/
public void setRunningQueue(boolean runningQueue) {
this.runningQueue = runningQueue;
}
/**
* Gets whether the mini-map flag should be reset.
* @return Whether mini-map flag should be reset.
*/
public boolean isMinimapFlagReset() {
return minimapFlagReset;
}
/**
* Set whether the mini-map flag should be reset.
* {@link isMinimapFlagReset()} will be equal to minimapFlagReset.
*
* @param minimapFlagReset Whether the mini-map flag should be reset.
*/
public void setMinimapFlagReset(boolean minimapFlagReset) {
this.minimapFlagReset = minimapFlagReset;
}
}