mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-28 05:45:10 -06:00
RSMod pathfinder port
This commit is contained in:
parent
dd56c211de
commit
0c196aa9e3
16 changed files with 502 additions and 1776 deletions
|
|
@ -63,6 +63,12 @@
|
|||
<version>[1.4.0,)</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.rsmod</groupId>
|
||||
<artifactId>rsmod-pathfinder</artifactId>
|
||||
<version>4.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ public abstract class MovementPulse extends Pulse {
|
|||
dl.getX(), dl.getY(), target.size(), target.size(),
|
||||
0, // walkFlag - "can interact from any unblocked direction"
|
||||
ml.getZ(),
|
||||
RegionManager::getClippingFlag
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -313,7 +313,7 @@ public abstract class MovementPulse extends Pulse {
|
|||
dl.getX(), dl.getY(), target.size(), target.size(),
|
||||
0, // walkFlag - "can interact from any unblocked direction"
|
||||
ml.getZ(),
|
||||
RegionManager::getClippingFlag
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +435,8 @@ public abstract class MovementPulse extends Pulse {
|
|||
if (i == size - 1 && interactLocation == null)
|
||||
interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ());
|
||||
}
|
||||
} else if (interactLocation == null && path.isSuccessful() && !path.isMoveNear()) {
|
||||
interactLocation = mover.getLocation();
|
||||
}
|
||||
previousLoc = loc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,15 +253,14 @@ class ScriptProcessor(val entity: Entity) {
|
|||
is Scenery -> {
|
||||
val basicPath = Pathfinder.find(entity, interactTarget)
|
||||
val path = basicPath.points.lastOrNull()
|
||||
if (basicPath.isMoveNear) {
|
||||
target.location
|
||||
return
|
||||
}
|
||||
if (path == null) {
|
||||
if (!basicPath.isSuccessful) {
|
||||
clearScripts(entity)
|
||||
return
|
||||
}
|
||||
Location.create(path.x, path.y, entity.location.z)
|
||||
if (basicPath.isMoveNear) {
|
||||
return
|
||||
}
|
||||
path?.let { Location.create(it.x, it.y, entity.location.z) } ?: entity.location
|
||||
}
|
||||
is GroundItem -> DestinationFlag.ITEM.getDestination(entity, interactTarget)
|
||||
else -> target.location
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package core.game.node.entity.npc
|
|||
import core.game.node.item.Item
|
||||
import core.api.ContentInterface
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.combat.BattleState
|
||||
import core.game.node.entity.combat.CombatStyle
|
||||
|
|
@ -24,12 +23,6 @@ open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
|
|||
}
|
||||
}
|
||||
|
||||
object StandardClipMaskSupplier : ClipMaskSupplier {
|
||||
override fun getClippingFlag (z: Int, x: Int, y: Int) : Int {
|
||||
return RegionManager.getClippingFlag(z,x,y)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every tick, before the base NPC tick() method.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
|
|
@ -144,7 +137,7 @@ open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
|
|||
* Called by pathfinding code to determine the clipping mask supplier this NPC should use. You can use this to ignore water, etc.
|
||||
*/
|
||||
open fun getClippingSupplier(self: NPC) : ClipMaskSupplier? {
|
||||
return StandardClipMaskSupplier
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ object ServerConfigParser {
|
|||
wild_pvp_enabled = data.getBoolean("world.wild_pvp_enabled"),
|
||||
jad_practice_enabled = data.getBoolean("world.jad_practice_enabled"),
|
||||
ge_announcement_limit = data.getLong("world.ge_announcement_limit", 500L).toInt(),
|
||||
smartpathfinder_bfs = data.getBoolean("world.smartpathfinder_bfs", false),
|
||||
enable_castle_wars = data.getBoolean("world.enable_castle_wars", false),
|
||||
message_model = data.getString("world.motw_identifier").toInt(),
|
||||
message_string = data.getString("world.motw_text").replace("@name", ServerConstants.SERVER_NAME)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ class GameSettings
|
|||
var wild_pvp_enabled: Boolean,
|
||||
var jad_practice_enabled: Boolean,
|
||||
var ge_announcement_limit: Int,
|
||||
var smartpathfinder_bfs: Boolean,
|
||||
var enable_castle_wars: Boolean,
|
||||
|
||||
/**"Lobby" interface
|
||||
|
|
@ -135,7 +134,6 @@ class GameSettings
|
|||
val wild_pvp_enabled = if(data.containsKey("wild_pvp_enabled")) data["wild_pvp_enabled"] as Boolean else true
|
||||
val jad_practice_enabled = if(data.containsKey("jad_practice_enabled")) data["jad_practice_enabled"] as Boolean else true
|
||||
val ge_announcement_limit = data["ge_announcement_limit"].toString().toInt()
|
||||
val smartpathfinder_bfs = if(data.containsKey("smartpathfinder_bfs")) data["smartpathfinder_bfs"] as Boolean else false
|
||||
val enable_castle_wars = if(data.containsKey("enable_castle_wars")) data["enable_castle_wars"] as Boolean else false
|
||||
val allow_token_purchase = data["allow_token_purchase"] as Boolean
|
||||
val message_of_the_week_identifier = data["message_of_the_week_identifier"].toString().toInt()
|
||||
|
|
@ -165,7 +163,6 @@ class GameSettings
|
|||
wild_pvp_enabled,
|
||||
jad_practice_enabled,
|
||||
ge_announcement_limit,
|
||||
smartpathfinder_bfs,
|
||||
enable_castle_wars,
|
||||
message_of_the_week_identifier,
|
||||
message_of_the_week_text
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import core.game.world.map.zone.ZoneBorders
|
|||
import core.tools.Log
|
||||
import core.tools.RandomFunction
|
||||
import core.tools.SystemLogger
|
||||
import org.rsmod.game.pathfinder.collision.CollisionFlagMap
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
|
@ -24,8 +25,11 @@ object RegionManager {
|
|||
* The region cache mapping.
|
||||
*/
|
||||
private val REGION_CACHE: MutableMap<Int, Region> = HashMap()
|
||||
private val RSMOD_PROJECTILE_REGIONS = HashSet<Int>()
|
||||
@JvmStatic val CLIPPING_FLAGS = HashMap<Int, Array<Int>>()
|
||||
@JvmStatic val PROJECTILE_FLAGS = HashMap<Int, Array<Int>>()
|
||||
@JvmStatic val RSMOD_CLIPPING_FLAGS = CollisionFlagMap()
|
||||
@JvmStatic val RSMOD_PROJECTILE_FLAGS = CollisionFlagMap()
|
||||
|
||||
public val LOCK = ReentrantLock()
|
||||
|
||||
|
|
@ -125,16 +129,69 @@ object RegionManager {
|
|||
|
||||
@JvmStatic
|
||||
fun getFlags(regionId: Int, projectile: Boolean) : Array<Int> {
|
||||
return if (projectile)
|
||||
PROJECTILE_FLAGS.getOrPut (regionId) {Array(16384){0}}
|
||||
else
|
||||
return if (projectile) {
|
||||
initialiseRsmodProjectileRegion(regionId)
|
||||
PROJECTILE_FLAGS.getOrPut(regionId) { Array(16384) { 0 } }
|
||||
} else {
|
||||
CLIPPING_FLAGS.getOrPut (regionId) {Array(16384){-1}}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun resetFlags(regionId: Int) {
|
||||
PROJECTILE_FLAGS.put (regionId, Array(16384){0})
|
||||
CLIPPING_FLAGS.put (regionId, Array(16384){-1})
|
||||
resetRsmodFlags(regionId)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setRsmodFlag(z: Int, x: Int, y: Int, projectile: Boolean, flag: Int) {
|
||||
val flags = if (projectile) RSMOD_PROJECTILE_FLAGS else RSMOD_CLIPPING_FLAGS
|
||||
flags[x, y, z] = flag
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun loadClippingWindow(center: Location, size: Int) {
|
||||
val minX = center.x - (size / 2)
|
||||
val minY = center.y - (size / 2)
|
||||
val maxX = minX + size - 1
|
||||
val maxY = minY + size - 1
|
||||
for (regionX in (minX shr 6)..(maxX shr 6)) {
|
||||
for (regionY in (minY shr 6)..(maxY shr 6)) {
|
||||
val regionId = (regionX shl 8) or regionY
|
||||
Region.load(forId(regionId))
|
||||
initialiseRsmodProjectileRegion(regionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetRsmodFlags(regionId: Int) {
|
||||
val baseX = (regionId shr 8) shl 6
|
||||
val baseY = (regionId and 0xFF) shl 6
|
||||
for (z in 0 until 4) {
|
||||
for (x in baseX until baseX + 64 step 8) {
|
||||
for (y in baseY until baseY + 64 step 8) {
|
||||
RSMOD_CLIPPING_FLAGS.deallocateIfPresent(x, y, z)
|
||||
val projectileFlags = RSMOD_PROJECTILE_FLAGS.allocateIfAbsent(x, y, z)
|
||||
projectileFlags.fill(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialiseRsmodProjectileRegion(regionId: Int) {
|
||||
if (!RSMOD_PROJECTILE_REGIONS.add(regionId)) {
|
||||
return
|
||||
}
|
||||
val baseX = (regionId shr 8) shl 6
|
||||
val baseY = (regionId and 0xFF) shl 6
|
||||
for (z in 0 until 4) {
|
||||
for (x in baseX until baseX + 64 step 8) {
|
||||
for (y in baseY until baseY + 64 step 8) {
|
||||
RSMOD_PROJECTILE_FLAGS.allocateIfAbsent(x, y, z).fill(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -179,7 +179,9 @@ public final class RegionFlags {
|
|||
public void addFlag(int x, int y, int clipdata) {
|
||||
int current = getFlag(x, y);
|
||||
Pair<Integer, Integer> indices = getFlagIndex(x, y);
|
||||
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = max(0, current) | clipdata;
|
||||
int updated = max(0, current) | clipdata;
|
||||
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = updated;
|
||||
RegionManager.setRsmodFlag(plane, baseX + x, baseY + y, projectile, updated);
|
||||
}
|
||||
|
||||
public void removeFlag(int x, int y, int clipdata) {
|
||||
|
|
@ -189,16 +191,19 @@ public final class RegionFlags {
|
|||
current = max(0, current) & ~clipdata;
|
||||
|
||||
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = current;
|
||||
RegionManager.setRsmodFlag(plane, baseX + x, baseY + y, projectile, current);
|
||||
}
|
||||
|
||||
public void clearFlag(int x, int y) {
|
||||
Pair<Integer, Integer> indices = getFlagIndex(x, y);
|
||||
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = 0;
|
||||
RegionManager.setRsmodFlag(plane, baseX + x, baseY + y, projectile, 0);
|
||||
}
|
||||
|
||||
public void invalidateFlag(int x, int y) {
|
||||
Pair<Integer, Integer> indices = getFlagIndex(x, y);
|
||||
RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = -1;
|
||||
RegionManager.setRsmodFlag(plane, baseX + x, baseY + y, projectile, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -534,4 +539,4 @@ public final class RegionFlags {
|
|||
public void setLandscape(boolean[][] landscape) {
|
||||
this.landscape = landscape;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,427 +0,0 @@
|
|||
package core.game.world.map.path;
|
||||
|
||||
import core.game.world.map.Direction;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.Point;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A pathfinder implementation used for an easy path, where the pathfinder won't
|
||||
* find a way around clipped objects.. <br> This is used for NPC combat
|
||||
* following, NPC random movement, etc.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class DumbPathfinder extends Pathfinder {
|
||||
/**
|
||||
* If a path can be found.
|
||||
*/
|
||||
private boolean found;
|
||||
|
||||
/**
|
||||
* The plane.
|
||||
*/
|
||||
private int z;
|
||||
|
||||
/**
|
||||
* The x-coordinate.
|
||||
*/
|
||||
private int x;
|
||||
|
||||
/**
|
||||
* The y-coordinate.
|
||||
*/
|
||||
private int y;
|
||||
|
||||
@Override
|
||||
public Path find(Location start, int size, Location end, int sizeX, int sizeY, int rotation, int type, int walkingFlag, boolean near, ClipMaskSupplier clipMaskSupplier) {
|
||||
Path path = new Path();
|
||||
z = start.getZ();
|
||||
x = start.getX();
|
||||
y = start.getY();
|
||||
List<Point> points = new ArrayList<>(20);
|
||||
path.setSuccesful(true);
|
||||
while (x != end.getX() || y != end.getY()) {
|
||||
Direction[] directions = getDirection(x, y, end);
|
||||
if (type != 0) {
|
||||
if ((type < 5 || type == 10) && canDoorInteract(x, y, size, end.getX(), end.getY(), type - 1, rotation, z, clipMaskSupplier)) {
|
||||
break;
|
||||
}
|
||||
if (type < 10 && canDecorationInteract(x, y, size, end.getX(), end.getY(), type - 1, rotation, z, clipMaskSupplier)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sizeX != 0 && sizeY != 0) {
|
||||
if (canInteract(x, y, size, end.getX(), end.getY(), sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) {
|
||||
break;
|
||||
}
|
||||
if (directions.length > 1) { // Ensures we approach the location
|
||||
// correctly (non-diagonal).
|
||||
Direction dir = directions[0];
|
||||
if (x + dir.getStepX() == end.getX() && y + dir.getStepY() == end.getY()) {
|
||||
directions[0] = directions[directions.length - 1];
|
||||
directions[directions.length - 1] = dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
found = true;
|
||||
if (size < 2) {
|
||||
checkSingleTraversal(points, clipMaskSupplier, directions);
|
||||
} else if (size == 2) {
|
||||
checkDoubleTraversal(points, clipMaskSupplier, directions);
|
||||
} else {
|
||||
checkVariableTraversal(points, directions, size, clipMaskSupplier);
|
||||
}
|
||||
if (!found) {
|
||||
path.setMoveNear(x != start.getX() || y != start.getY());
|
||||
path.setSuccesful(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!points.isEmpty()) {
|
||||
Direction last = null;
|
||||
for (int i = 0; i < points.size() - 1; i++) {
|
||||
Point p = points.get(i);
|
||||
path.getPoints().add(p);
|
||||
}
|
||||
path.getPoints().add(points.get(points.size() - 1));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks traversal for a size 1 entity.
|
||||
* @param points The points list.
|
||||
* @param directions The directions.
|
||||
*/
|
||||
private void checkSingleTraversal(List<Point> points, ClipMaskSupplier clipMaskSupplier, Direction... directions) {
|
||||
for (Direction dir : directions) {
|
||||
found = true;
|
||||
switch (dir) {
|
||||
case NORTH:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x, y + 1, dir));
|
||||
y++;
|
||||
break;
|
||||
case NORTH_EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y + 1) & PREVENT_NORTHEAST) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x + 1, y + 1, dir));
|
||||
x++;
|
||||
y++;
|
||||
break;
|
||||
case EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x + 1, y, dir));
|
||||
x++;
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & PREVENT_SOUTHEAST) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x + 1, y - 1, dir));
|
||||
x++;
|
||||
y--;
|
||||
break;
|
||||
case SOUTH:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x, y - 1, dir));
|
||||
y--;
|
||||
break;
|
||||
case SOUTH_WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & PREVENT_SOUTHWEST) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x - 1, y - 1, dir));
|
||||
x--;
|
||||
y--;
|
||||
break;
|
||||
case WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x - 1, y, dir));
|
||||
x--;
|
||||
break;
|
||||
case NORTH_WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & PREVENT_NORTHWEST) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x - 1, y + 1, dir));
|
||||
x--;
|
||||
y++;
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks traversal for a size 1 entity.
|
||||
* @param points The points list.
|
||||
* @param directions The directions.
|
||||
*/
|
||||
private void checkDoubleTraversal(List<Point> points, ClipMaskSupplier clipMaskSupplier, Direction... directions) {
|
||||
for (Direction dir : directions) {
|
||||
found = true;
|
||||
switch (dir) {
|
||||
case NORTH:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x, y + 2) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y + 2) & 0x12c01e0) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x, y + 1, dir));
|
||||
y++;
|
||||
break;
|
||||
case NORTH_EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y + 2) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 2, y + 2) & 0x12c01e0) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 2, y + 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x + 1, y + 1, dir));
|
||||
x++;
|
||||
y++;
|
||||
break;
|
||||
case EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 2, y) & 0x12c0183) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 2, y + 1) & 0x12c01e0) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x + 1, y, dir));
|
||||
x++;
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 2, y) & 0x12c01e0) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 2, y - 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x + 1, y - 1, dir));
|
||||
x++;
|
||||
y--;
|
||||
break;
|
||||
case SOUTH:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x, y - 1, dir));
|
||||
y--;
|
||||
break;
|
||||
case SOUTH_WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x - 1, y - 1, dir));
|
||||
x--;
|
||||
y--;
|
||||
break;
|
||||
case WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & 0x12c0138) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x - 1, y, dir));
|
||||
x--;
|
||||
break;
|
||||
case NORTH_WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 2) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 2) & 0x12c01e0) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
points.add(new Point(x - 1, y + 1, dir));
|
||||
x--;
|
||||
y++;
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks traversal for variable size entities.
|
||||
* @param points The points list.
|
||||
* @param directions The directions to check.
|
||||
* @param size The mover size.
|
||||
*/
|
||||
private void checkVariableTraversal(List<Point> points, Direction[] directions, int size, ClipMaskSupplier clipMaskSupplier) {
|
||||
for (Direction dir : directions) {
|
||||
found = true;
|
||||
roar: switch (dir) {
|
||||
case NORTH:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x, y + size) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x + (size - 1), y + size) & 0x12c01e0) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + i, y + size) & 0x12c01f8) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x, y + 1, dir));
|
||||
y++;
|
||||
break;
|
||||
case NORTH_EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y + size) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x + size, y + size) & 0x12c01e0) != 0 || (clipMaskSupplier.getClippingFlag(z, x + size, y + 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + (i + 1), y + size) & 0x12c01f8) != 0 || (clipMaskSupplier.getClippingFlag(z, x + size, y + (i + 1)) & 0x12c01e3) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x + 1, y + 1, dir));
|
||||
x++;
|
||||
y++;
|
||||
break;
|
||||
case EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + size, y) & 0x12c0183) != 0 || (clipMaskSupplier.getClippingFlag(z, x + size, y + (size - 1)) & 0x12c01e0) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + size, y + i) & 0x12c01e3) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x + 1, y, dir));
|
||||
x++;
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + size, y + (size - 2)) & 0x12c01e0) != 0 || (clipMaskSupplier.getClippingFlag(z, x + size, y - 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + size, y + (i - 1)) & 0x12c01e3) != 0 || (clipMaskSupplier.getClippingFlag(z, x + (i + 1), y - 1) & 0x12c018f) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x + 1, y - 1, dir));
|
||||
x++;
|
||||
y--;
|
||||
break;
|
||||
case SOUTH:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + (size - 1), y - 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x + i, y - 1) & 0x12c018f) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x, y - 1, dir));
|
||||
y--;
|
||||
break;
|
||||
case SOUTH_WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y + (size - 2)) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + (size - 2), y - 1) & 0x12c0183) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y + (i - 1)) & 0x12c013e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + (i - 1), y - 1) & 0x12c018f) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x - 1, y - 1, dir));
|
||||
x--;
|
||||
y--;
|
||||
break;
|
||||
case WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + (size - 1)) & 0x12c0138) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y + i) & 0x12c013e) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x - 1, y, dir));
|
||||
x--;
|
||||
break;
|
||||
case NORTH_WEST:
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & 0x12c010e) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + size) & 0x12c0138) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + size) & 0x12c01e0) != 0) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
for (int i = 1; i < size - 1; i++) {
|
||||
if ((clipMaskSupplier.getClippingFlag(z, x - 1, y + (i + 1)) & 0x12c013e) != 0 || (clipMaskSupplier.getClippingFlag(z, x + (i - 1), y + size) & 0x12c01f8) != 0) {
|
||||
found = false;
|
||||
break roar;
|
||||
}
|
||||
}
|
||||
points.add(new Point(x - 1, y + 1, dir));
|
||||
x--;
|
||||
y++;
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the direction.
|
||||
* @param end The end direction.
|
||||
* @return The direction.
|
||||
*/
|
||||
private static Direction[] getDirection(int startX, int startY, Location end) {
|
||||
int endX = end.getX();
|
||||
int endY = end.getY();
|
||||
if (startX == endX) {
|
||||
if (startY > endY) {
|
||||
return new Direction[] { Direction.SOUTH };
|
||||
} else if (startY < endY) {
|
||||
return new Direction[] { Direction.NORTH };
|
||||
}
|
||||
} else if (startY == endY) {
|
||||
if (startX > endX) {
|
||||
return new Direction[] { Direction.WEST };
|
||||
}
|
||||
return new Direction[] { Direction.EAST };
|
||||
} else {
|
||||
if (startX < endX && startY < endY) {
|
||||
return new Direction[] { Direction.NORTH_EAST, Direction.EAST, Direction.NORTH };
|
||||
} else if (startX < endX && startY > endY) {
|
||||
return new Direction[] { Direction.SOUTH_EAST, Direction.EAST, Direction.SOUTH };
|
||||
} else if (startX > endX && startY < endY) {
|
||||
return new Direction[] { Direction.NORTH_WEST, Direction.WEST, Direction.NORTH };
|
||||
} else if (startX > endX && startY > endY) {
|
||||
return new Direction[] { Direction.SOUTH_WEST, Direction.WEST, Direction.SOUTH };
|
||||
}
|
||||
}
|
||||
return new Direction[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import core.game.node.entity.Entity;
|
|||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.item.GroundItem;
|
||||
import core.game.node.scenery.Scenery;
|
||||
import core.game.world.map.Direction;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.RegionManager;
|
||||
|
||||
|
|
@ -23,71 +22,17 @@ public abstract class Pathfinder {
|
|||
/**
|
||||
* The smart path finder.
|
||||
*/
|
||||
public static final SmartPathfinder SMART = new SmartPathfinder();
|
||||
public static final Pathfinder SMART = new RsmodPathfinder();
|
||||
|
||||
/**
|
||||
* The dumb path finder.
|
||||
*/
|
||||
public static final DumbPathfinder DUMB = new DumbPathfinder();
|
||||
public static final Pathfinder DUMB = new RsmodPathfinder();
|
||||
|
||||
/**
|
||||
* The projectile path finder.
|
||||
*/
|
||||
public static final ProjectilePathfinder PROJECTILE = new ProjectilePathfinder();
|
||||
|
||||
/**
|
||||
* The south direction flag.
|
||||
*/
|
||||
public static final int SOUTH_FLAG = 0x1;
|
||||
|
||||
/**
|
||||
* The west direction flag.
|
||||
*/
|
||||
public static final int WEST_FLAG = 0x2;
|
||||
|
||||
/**
|
||||
* The north direction flag.
|
||||
*/
|
||||
public static final int NORTH_FLAG = 0x4;
|
||||
|
||||
/**
|
||||
* The east direction flag.
|
||||
*/
|
||||
public static final int EAST_FLAG = 0x8;
|
||||
|
||||
/**
|
||||
* The south-west direction flag.
|
||||
*/
|
||||
public static final int SOUTH_WEST_FLAG = SOUTH_FLAG | WEST_FLAG;
|
||||
|
||||
/**
|
||||
* The north-west direction flag.
|
||||
*/
|
||||
public static final int NORTH_WEST_FLAG = NORTH_FLAG | WEST_FLAG;
|
||||
|
||||
/**
|
||||
* The south-east direction flag.
|
||||
*/
|
||||
public static final int SOUTH_EAST_FLAG = SOUTH_FLAG | EAST_FLAG;
|
||||
|
||||
/**
|
||||
* The north-east direction flag.
|
||||
*/
|
||||
public static final int NORTH_EAST_FLAG = NORTH_FLAG | EAST_FLAG;
|
||||
|
||||
public static int flagForDirection(Direction d) {
|
||||
switch(d) {
|
||||
case NORTH_WEST: return NORTH_WEST_FLAG;
|
||||
case NORTH: return NORTH_FLAG;
|
||||
case NORTH_EAST: return NORTH_EAST_FLAG;
|
||||
case WEST: return WEST_FLAG;
|
||||
case EAST: return EAST_FLAG;
|
||||
case SOUTH_WEST: return SOUTH_WEST_FLAG;
|
||||
case SOUTH: return SOUTH_FLAG;
|
||||
case SOUTH_EAST: return SOUTH_EAST_FLAG;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
public static final Pathfinder PROJECTILE = new RsmodProjectilePathfinder();
|
||||
|
||||
/**
|
||||
* Finds a path from the location to the end location.
|
||||
|
|
@ -125,12 +70,11 @@ public abstract class Pathfinder {
|
|||
* @return The path.
|
||||
*/
|
||||
public static Path find(Entity mover, Node destination, boolean near, Pathfinder finder) {
|
||||
ClipMaskSupplier cms = null;
|
||||
if (mover instanceof NPC) {
|
||||
cms = ((NPC) mover).behavior.getClippingSupplier(((NPC) mover));
|
||||
}
|
||||
if (cms == null)
|
||||
cms = RegionManager::getClippingFlag;
|
||||
ClipMaskSupplier cms = null;
|
||||
if (mover instanceof NPC) {
|
||||
NPC npc = (NPC) mover;
|
||||
cms = npc.behavior != null ? npc.behavior.getClippingSupplier(npc) : null;
|
||||
}
|
||||
return find(mover.getLocation(), mover.size(), destination, near, finder, cms);
|
||||
}
|
||||
|
||||
|
|
@ -151,8 +95,8 @@ public abstract class Pathfinder {
|
|||
return find(start, destination, true, SMART);
|
||||
}
|
||||
|
||||
public static Path find(Location start, Node destination, int moverSize) {
|
||||
return find(start, moverSize, destination, true, SMART, RegionManager::getClippingFlag);
|
||||
public static Path find(Location start, Node destination, int moverSize) {
|
||||
return find(start, moverSize, destination, true, SMART, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -164,7 +108,7 @@ public abstract class Pathfinder {
|
|||
* @return The path.
|
||||
*/
|
||||
public static Path find(Location start, Node destination, boolean near, Pathfinder finder) {
|
||||
return find(start, 1, destination, near, finder, RegionManager::getClippingFlag);
|
||||
return find(start, 1, destination, near, finder, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,19 +125,9 @@ public abstract class Pathfinder {
|
|||
int type = object.getType();
|
||||
int rotation = object.getRotation();
|
||||
if (type == 10 || type == 11 || type == 22) {
|
||||
int sizeX = object.getDefinition().sizeX;
|
||||
int sizeY = object.getDefinition().sizeY;
|
||||
if (rotation % 2 != 0) {
|
||||
sizeX = object.getDefinition().sizeY;
|
||||
sizeY = object.getDefinition().sizeX;
|
||||
}
|
||||
int walkingFlag = object.getDefinition().getWalkingFlag();
|
||||
if (rotation != 0) {
|
||||
walkingFlag = (walkingFlag << rotation & 0xf) + (walkingFlag >> 4 - rotation);
|
||||
}
|
||||
return finder.find(start, moverSize, destination.getLocation(), sizeX, sizeY, 0, 0, walkingFlag, near, clipMaskSupplier);
|
||||
return finder.find(start, moverSize, destination.getLocation(), object.getDefinition().sizeX, object.getDefinition().sizeY, rotation, type, object.getDefinition().getWalkingFlag(), near, clipMaskSupplier);
|
||||
}
|
||||
return finder.find(start, moverSize, destination.getLocation(), 0, 0, rotation, 1 + type, 0, near, clipMaskSupplier);
|
||||
return finder.find(start, moverSize, destination.getLocation(), 0, 0, rotation, type, 0, near, clipMaskSupplier);
|
||||
}
|
||||
int size = 0;
|
||||
if (destination instanceof Entity) {
|
||||
|
|
@ -201,7 +135,7 @@ public abstract class Pathfinder {
|
|||
} else if (destination instanceof GroundItem && !RegionManager.isTeleportPermitted(destination.getLocation())) {
|
||||
size = 1;
|
||||
}
|
||||
return finder.find(start, moverSize, destination.getLocation(), size, size, 0, 0, 0, near, clipMaskSupplier);
|
||||
return finder.find(start, moverSize, destination.getLocation(), size, size, 0, -1, 0, near, clipMaskSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -216,116 +150,7 @@ public abstract class Pathfinder {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public static boolean canDecorationInteract(int curX, int curY, int size, int destX, int destY, int rotation, int type, int z, ClipMaskSupplier clipMaskSupplier) {
|
||||
if (size != 1) {
|
||||
if (destX >= curX && destX <= (curX + size) - 1 && destY <= (destY + size) - 1) {
|
||||
return true;
|
||||
}
|
||||
} else if (destX == curX && curY == destY) {
|
||||
return true;
|
||||
}
|
||||
if (size == 1) {
|
||||
int flag = clipMaskSupplier.getClippingFlag(z, curX, curY);
|
||||
if (type == 6 || type == 7) {
|
||||
if (type == 7) {
|
||||
rotation = rotation + 2 & 0x3;
|
||||
}
|
||||
if (rotation == 0) {
|
||||
if (curX == 1 + destX && curY == destY && (0x80 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && curY == destY - 1 && (flag & 0x2) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 1) {
|
||||
if (curX == destX - 1 && curY == destY && (0x8 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && curY == destY - 1 && (flag & 0x2) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 2) {
|
||||
if (destX - 1 == curX && destY == curY && (flag & 0x8) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && destY + 1 == curY && (0x20 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 3) {
|
||||
if (destX + 1 == curX && curY == destY && (0x80 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && curY == destY + 1 && (0x20 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == 8) {
|
||||
if (destX == curX && curY == destY + 1 && (flag & 0x20) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && -1 + destY == curY && (0x2 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX - 1 && curY == destY && (0x8 & flag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX + 1 && curY == destY && (flag & 0x80) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int cornerX = curX + size - 1;
|
||||
int cornerY = curY + size - 1;
|
||||
if (type == 6 || type == 7) {
|
||||
if (type == 7) {
|
||||
rotation = 0x3 & 2 + rotation;
|
||||
}
|
||||
if (rotation == 0) {
|
||||
if (destX + 1 == curX && destY >= curY && destY <= cornerY && (clipMaskSupplier.getClippingFlag(z, curX, destY) & 0x80) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && destX <= cornerX && destY - size == curY && (0x2 & clipMaskSupplier.getClippingFlag(z, destX, cornerY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 1) {
|
||||
if (-size + destX == curX && destY >= curY && cornerY >= destY && (clipMaskSupplier.getClippingFlag(z, cornerX, destY) & 0x8) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && cornerX >= destX && -size + destY == curY && (clipMaskSupplier.getClippingFlag(z, destX, cornerY) & 0x2) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 2) {
|
||||
if (curX == destX - size && curY <= destY && destY <= cornerY && (0x8 & clipMaskSupplier.getClippingFlag(z, cornerX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && cornerX >= destX && destY + 1 == curY && (0x20 & clipMaskSupplier.getClippingFlag(z, destX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 3) {
|
||||
if (1 + destX == curX && curY <= destY && destY <= cornerY && (0x80 & clipMaskSupplier.getClippingFlag(z, curX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && destX <= cornerX && 1 + destY == curY && (clipMaskSupplier.getClippingFlag(z, destX, curY) & 0x20) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == 8) {
|
||||
if (curX <= destX && destX <= cornerX && 1 + destY == curY && (clipMaskSupplier.getClippingFlag(z, destX, curY) & 0x20) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && destX <= cornerX && curY == -size + destY && (0x2 & clipMaskSupplier.getClippingFlag(z, destX, cornerY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == -size + destX && destY >= curY && destY <= cornerY && (0x8 & clipMaskSupplier.getClippingFlag(z, cornerX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (1 + destX == curX && curY <= destY && cornerY >= destY && (clipMaskSupplier.getClippingFlag(z, curX, destY) & 0x80) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return RsmodPathfinder.canReach(curX, curY, size, destX, destY, 1, 1, rotation, type, 0, z, clipMaskSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -340,242 +165,7 @@ public abstract class Pathfinder {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public static boolean canDoorInteract(int curX, int curY, int size, int destX, int destY, int type, int rotation, int z, ClipMaskSupplier clipMaskSupplier) {
|
||||
if (size != 1) {
|
||||
if (destX >= curX && destX <= size + curX - 1 && destY <= destY + size - 1) {
|
||||
return true;
|
||||
}
|
||||
} else if (curX == destX && destY == curY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
if (type == 0) {
|
||||
if (rotation == 0) {
|
||||
if (curX == destX - 1 && destY == curY) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && 1 + destY == curY && (0x12c0120 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && destY - 1 == curY && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x12c0102) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 1) {
|
||||
if (curX == destX && destY + 1 == curY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX - 1 && curY == destY && (0x12c0108 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == 1 + destX && destY == curY && (0x12c0180 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 2) {
|
||||
if (1 + destX == curX && destY == curY) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && 1 + destY == curY && (0x12c0120 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && curY == destY - 1 && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x12c0102) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 3) {
|
||||
if (curX == destX && -1 + destY == curY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == -1 + destX && destY == curY && (0x12c0108 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == 1 + destX && destY == curY && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x12c0180) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type == 2) {
|
||||
if (rotation == 0) {
|
||||
if (destX - 1 == curX && curY == destY) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && curY == 1 + destY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX + 1 && curY == destY && (0x12c0180 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && destY - 1 == curY && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x12c0102) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 1) {
|
||||
if (curX == destX - 1 && curY == destY && (0x12c0108 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && curY == 1 + destY) {
|
||||
return true;
|
||||
}
|
||||
if (1 + destX == curX && curY == destY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && destY - 1 == curY && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x12c0102) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 2) {
|
||||
if (destX - 1 == curX && destY == curY && (0x12c0108 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && 1 + destY == curY && (0x12c0120 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (1 + destX == curX && curY == destY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && curY == destY - 1) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 3) {
|
||||
if (destX - 1 == curX && curY == destY) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && curY == destY + 1 && (0x12c0120 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == 1 + destX && curY == destY && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x12c0180) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX == curX && destY - 1 == curY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type == 9) {
|
||||
if (curX == destX && curY == destY + 1 && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x20) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX && curY == destY - 1 && (clipMaskSupplier.getClippingFlag(z, curX, curY) & 0x2) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX - 1 && curY == destY && (0x8 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX + 1 == curX && curY == destY && (0x80 & clipMaskSupplier.getClippingFlag(z, curX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int cornerX = curX - (1 - size);
|
||||
int cornerY = -1 + curY + size;
|
||||
if (type == 0) {
|
||||
if (rotation == 0) {
|
||||
if (destX - size == curX && destY >= curY && destY <= cornerY) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && cornerX >= destX && curY == 1 + destY && (clipMaskSupplier.getClippingFlag(z, destX, curY) & 0x12c0120) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && cornerX >= destX && destY - size == curY && (clipMaskSupplier.getClippingFlag(z, destX, cornerY) & 0x12c0102) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 1) {
|
||||
if (destX >= curX && cornerX >= destX && destY + 1 == curY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == -size + destX && destY >= curY && cornerY >= destY && (0x12c0108 & clipMaskSupplier.getClippingFlag(z, cornerX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == 1 + destX && destY >= curY && cornerY >= destY && (clipMaskSupplier.getClippingFlag(z, curX, destY) & 0x12c0180) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 2) {
|
||||
if (curX == 1 + destX && curY <= destY && destY <= cornerY) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && cornerX >= destX && destY + 1 == curY && (0x12c0120 & clipMaskSupplier.getClippingFlag(z, destX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && destX <= cornerX && destY - size == curY && (0x12c0102 & clipMaskSupplier.getClippingFlag(z, destX, cornerY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 3) {
|
||||
if (curX <= destX && destX <= cornerX && curY == -size + destY) {
|
||||
return true;
|
||||
}
|
||||
if (-size + destX == curX && curY <= destY && destY <= cornerY && (clipMaskSupplier.getClippingFlag(z, cornerX, destY) & 0x12c0108) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (1 + destX == curX && curY <= destY && cornerY >= destY && (clipMaskSupplier.getClippingFlag(z, curX, destY) & 0x12c0180) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == 2) {
|
||||
if (rotation == 0) {
|
||||
if (destX - size == curX && curY <= destY && destY <= cornerY) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && destX <= cornerX && curY == 1 + destY) {
|
||||
return true;
|
||||
}
|
||||
if (curX == 1 + destX && curY <= destY && destY <= cornerY && (0x12c0180 & clipMaskSupplier.getClippingFlag(z, curX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && cornerX >= destX && -size + destY == curY && (clipMaskSupplier.getClippingFlag(z, destX, cornerY) & 0x12c0102) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 1) {
|
||||
if (-size + destX == curX && destY >= curY && destY <= cornerY && (clipMaskSupplier.getClippingFlag(z, cornerX, destY) & 0x12c0108) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && cornerX >= destX && curY == 1 + destY) {
|
||||
return true;
|
||||
}
|
||||
if (destX + 1 == curX && curY <= destY && destY <= cornerY) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && cornerX >= destX && destY + -size == curY && (0x12c0102 & clipMaskSupplier.getClippingFlag(z, destX, cornerY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 2) {
|
||||
if (curX == destX - size && curY <= destY && cornerY >= destY && (clipMaskSupplier.getClippingFlag(z, cornerX, destY) & 0x12c0108) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && destX <= cornerX && 1 + destY == curY && (0x12c0120 & clipMaskSupplier.getClippingFlag(z, destX, curY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (1 + destX == curX && destY >= curY && cornerY >= destY) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && destX <= cornerX && curY == -size + destY) {
|
||||
return true;
|
||||
}
|
||||
} else if (rotation == 3) {
|
||||
if (destX + -size == curX && destY >= curY && destY <= cornerY) {
|
||||
return true;
|
||||
}
|
||||
if (curX <= destX && cornerX >= destX && curY == 1 + destY && (clipMaskSupplier.getClippingFlag(z, destX, curY) & 0x12c0120) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (1 + destX == curX && destY >= curY && cornerY >= destY && (0x12c0180 & clipMaskSupplier.getClippingFlag(z, curX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && destX <= cornerX && curY == -size + destY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == 9) {
|
||||
if (destX >= curX && destX <= cornerX && curY == 1 + destY && (clipMaskSupplier.getClippingFlag(z, destX, curY) & 0x12c0120) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (destX >= curX && cornerX >= destX && curY == -size + destY && (0x12c0102 & clipMaskSupplier.getClippingFlag(z, destX, cornerY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (-size + destX == curX && destY >= curY && cornerY >= destY && (0x12c0108 & clipMaskSupplier.getClippingFlag(z, cornerX, destY)) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (curX == destX + 1 && destY >= curY && cornerY >= destY && (clipMaskSupplier.getClippingFlag(z, curX, destY) & 0x12c0180) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return RsmodPathfinder.canReach(curX, curY, size, destX, destY, 1, 1, rotation, type, 0, z, clipMaskSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -613,28 +203,7 @@ public abstract class Pathfinder {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public static boolean canInteract(int x, int y, int moverSize, int destX, int destY, int sizeX, int sizeY, int walkFlag, int z, ClipMaskSupplier clipMaskSupplier) {
|
||||
if (moverSize > 1) {
|
||||
return isStandingIn(x, y, moverSize, moverSize, destX, destY, sizeX, sizeY) || canInteractSized(x, y, moverSize, moverSize, destX, destY, sizeX, sizeY, walkFlag, z);
|
||||
}
|
||||
int flag = clipMaskSupplier.getClippingFlag(z, x, y);
|
||||
int cornerX = destX + sizeX - 1;
|
||||
int cornerY = destY + sizeY - 1;
|
||||
if (destX <= x && cornerX >= x && y >= destY && y <= cornerY) {
|
||||
return true;
|
||||
}
|
||||
if (x == destX - 1 && destY <= y && y <= cornerY && (0x8 & flag) == 0 && (0x8 & walkFlag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (x == cornerX + 1 && destY <= y && cornerY >= y && (flag & 0x80) == 0 && (0x2 & walkFlag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (y == destY - 1 && destX <= x && cornerX >= x && (0x2 & flag) == 0 && (0x4 & walkFlag) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (y == cornerY + 1 && destX <= x && cornerX >= x && (flag & 0x20) == 0 && (0x1 & walkFlag) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return RsmodPathfinder.canReach(x, y, moverSize, destX, destY, sizeX, sizeY, 0, -1, walkFlag, z, clipMaskSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -646,73 +215,6 @@ public abstract class Pathfinder {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public static boolean canInteractSized(int curX, int curY, int moverSizeX, int moverSizeY, int destX, int destY, int sizeX, int sizeY, int walkingFlag, int z) {
|
||||
int fromCornerY = curY + moverSizeY;
|
||||
int fromCornerX = curX + moverSizeX;
|
||||
int toCornerX = sizeX + destX;
|
||||
int toCornerY = sizeY + destY;
|
||||
if (destX <= curX && curX < toCornerX) {
|
||||
if (destY == fromCornerY && (walkingFlag & 0x4) == 0) {
|
||||
int x = curX;
|
||||
for (int endX = toCornerX < fromCornerX ? toCornerX : fromCornerX; endX > x; x++) {
|
||||
if ((RegionManager.getClippingFlag(z, x, -1 + fromCornerY) & 0x2) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (toCornerY == curY && (walkingFlag & 0x1) == 0) {
|
||||
int x = curX;
|
||||
for (int endX = fromCornerX <= toCornerX ? fromCornerX : toCornerX; x < endX; x++) {
|
||||
if ((RegionManager.getClippingFlag(z, x, curY) & 0x20) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (destX < fromCornerX && toCornerX >= fromCornerX) {
|
||||
if (fromCornerY == destY && (0x4 & walkingFlag) == 0) {
|
||||
for (int x = destX; fromCornerX > x; x++) {
|
||||
if ((RegionManager.getClippingFlag(z, x, -1 + (fromCornerY)) & 0x2) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (toCornerY == curY && (0x1 & walkingFlag) == 0) {
|
||||
for (int x = destX; fromCornerX > x; x++) {
|
||||
if ((RegionManager.getClippingFlag(z, x, curY) & 0x20) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (curY < destY || curY >= toCornerY) {
|
||||
if (fromCornerY > destY && toCornerY >= fromCornerY) {
|
||||
if (fromCornerX == destX && (walkingFlag & 0x8) == 0) {
|
||||
for (int y = destY; y < fromCornerY; y++) {
|
||||
if ((RegionManager.getClippingFlag(z, -1 + fromCornerX, y) & 0x8) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (curX == toCornerX && (0x2 & walkingFlag) == 0) {
|
||||
for (int y = destY; fromCornerY > y; y++) {
|
||||
if ((RegionManager.getClippingFlag(z, curX, y) & 0x80) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (destX != fromCornerX || (0x8 & walkingFlag) != 0) {
|
||||
if (curX == toCornerX && (walkingFlag & 0x2) == 0) {
|
||||
int y = curY;
|
||||
for (int endY = fromCornerY <= toCornerY ? fromCornerY : toCornerY; y < endY; y++) {
|
||||
if ((0x80 & RegionManager.getClippingFlag(z, curX, y)) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int y = curY;
|
||||
for (int endY = fromCornerY > toCornerY ? toCornerY : fromCornerY; endY > y; y++) {
|
||||
if ((RegionManager.getClippingFlag(z, fromCornerX - 1, y) & 0x8) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return RsmodPathfinder.canReach(curX, curY, moverSizeX, destX, destY, sizeX, sizeY, 0, -1, walkingFlag, z, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,200 +0,0 @@
|
|||
package core.game.world.map.path;
|
||||
|
||||
import core.game.world.map.Direction;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.Point;
|
||||
import core.game.world.map.RegionManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A pathfinder implementation used for checking projectile paths.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class ProjectilePathfinder extends Pathfinder {
|
||||
|
||||
/**
|
||||
* If a path can be found.
|
||||
*/
|
||||
private boolean found;
|
||||
|
||||
/**
|
||||
* The plane.
|
||||
*/
|
||||
private int z;
|
||||
|
||||
/**
|
||||
* The x-coordinate.
|
||||
*/
|
||||
private int x;
|
||||
|
||||
/**
|
||||
* The y-coordinate.
|
||||
*/
|
||||
private int y;
|
||||
|
||||
@Override
|
||||
public Path find(Location start, int size, Location end, int sizeX, int sizeY, int rotation, int type, int walkingFlag, boolean near, ClipMaskSupplier clipMaskSupplier) {
|
||||
Path path = new Path();
|
||||
z = start.getZ();
|
||||
x = start.getX();
|
||||
y = start.getY();
|
||||
List<Point> points = new ArrayList<>(20);
|
||||
path.setSuccesful(true);
|
||||
while (x != end.getX() || y != end.getY()) {
|
||||
Direction[] directions = getDirection(x, y, end);
|
||||
found = true;
|
||||
checkSingleTraversal(points, directions);
|
||||
if (!found) {
|
||||
path.setMoveNear(x != start.getX() || y != start.getY());
|
||||
path.setSuccesful(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!points.isEmpty()) {
|
||||
for (int i = 0; i < points.size() - 1; i++) {
|
||||
Point p = points.get(i);
|
||||
if (p.getDirection() != null) {
|
||||
path.getPoints().add(p);
|
||||
}
|
||||
}
|
||||
path.getPoints().add(points.get(points.size() - 1));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks traversal for a size 1 entity.
|
||||
*
|
||||
* @param points The points list.
|
||||
* @param directions The directions.
|
||||
*/
|
||||
private void checkSingleTraversal(List<Point> points, Direction... directions) {
|
||||
dir:
|
||||
for (Direction dir : directions) {
|
||||
found = true;
|
||||
switch (dir) {
|
||||
case NORTH:
|
||||
if (flagged(z, x, y + 1, 0x12c0120)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x, y + 1, dir));
|
||||
y++;
|
||||
break;
|
||||
case NORTH_EAST:
|
||||
if (flagged(z, x + 1, y, 0x12c0180)
|
||||
|| flagged(z, x, y + 1, 0x12c0120)
|
||||
|| flagged(z, x + 1, y + 1, 0x12c01e0)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x + 1, y + 1, dir));
|
||||
x++;
|
||||
y++;
|
||||
break;
|
||||
case EAST:
|
||||
if (flagged(z, x + 1, y, 0x12c0180)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x + 1, y, dir));
|
||||
x++;
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
if (flagged(z, x + 1, y, 0x12c0180)
|
||||
|| flagged(z, x, y - 1, 0x12c0102)
|
||||
|| flagged(z, x + 1, y - 1, 0x12c0183)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x + 1, y - 1, dir));
|
||||
x++;
|
||||
y--;
|
||||
break;
|
||||
case SOUTH:
|
||||
if (flagged(z, x, y - 1, 0x12c0102)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x, y - 1, dir));
|
||||
y--;
|
||||
break;
|
||||
case SOUTH_WEST:
|
||||
if (flagged(z, x - 1, y, 0x12c0108)
|
||||
|| flagged(z, x, y - 1, 0x12c0102)
|
||||
|| flagged(z, x - 1, y - 1, 0x12c010e)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x - 1, y - 1, dir));
|
||||
x--;
|
||||
y--;
|
||||
break;
|
||||
case WEST:
|
||||
if (flagged(z, x - 1, y, 0x12c0108)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x - 1, y, dir));
|
||||
x--;
|
||||
break;
|
||||
case NORTH_WEST:
|
||||
if (flagged(z, x - 1, y, 0x12c0108)
|
||||
|| flagged(z, x, y + 1, 0x12c0120)
|
||||
|| flagged(z, x - 1, y + 1, 0x12c0138)) {
|
||||
found = false;
|
||||
break dir;
|
||||
}
|
||||
points.add(new Point(x - 1, y + 1, dir));
|
||||
x--;
|
||||
y++;
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean flagged(int z, int x, int y, int pFlagMask) {
|
||||
int pFlag = RegionManager.getProjectileFlag(z, x, y);
|
||||
return (pFlag & pFlagMask) != 0 || (pFlag & 0x20000) != 0 || (RegionManager.getClippingFlag(z, x, y) & 0x20000) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the direction.
|
||||
* @param startX The startX.
|
||||
* @param startY The startY.
|
||||
* @param end The end direction.
|
||||
* @return The direction.
|
||||
*/
|
||||
private static Direction[] getDirection(int startX, int startY, Location end) {
|
||||
int endX = end.getX();
|
||||
int endY = end.getY();
|
||||
if (startX == endX) {
|
||||
if (startY > endY) {
|
||||
return new Direction[] { Direction.SOUTH };
|
||||
} else if (startY < endY) {
|
||||
return new Direction[] { Direction.NORTH };
|
||||
}
|
||||
} else if (startY == endY) {
|
||||
if (startX > endX) {
|
||||
return new Direction[] { Direction.WEST };
|
||||
}
|
||||
return new Direction[] { Direction.EAST };
|
||||
} else {
|
||||
if (startX < endX && startY < endY) {
|
||||
return new Direction[] { Direction.NORTH_EAST, Direction.EAST, Direction.NORTH };
|
||||
} else if (startX < endX && startY > endY) {
|
||||
return new Direction[] { Direction.SOUTH_EAST, Direction.EAST, Direction.SOUTH };
|
||||
} else if (startX > endX && startY < endY) {
|
||||
return new Direction[] { Direction.NORTH_WEST, Direction.WEST, Direction.NORTH };
|
||||
} else if (startX > endX && startY > endY) {
|
||||
return new Direction[] { Direction.SOUTH_WEST, Direction.WEST, Direction.SOUTH };
|
||||
}
|
||||
}
|
||||
return new Direction[0];
|
||||
}
|
||||
}
|
||||
186
Server/src/main/core/game/world/map/path/RsmodPathfinder.kt
Normal file
186
Server/src/main/core/game/world/map/path/RsmodPathfinder.kt
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
package core.game.world.map.path
|
||||
|
||||
import core.ServerConstants
|
||||
import core.api.utils.Vector
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.Point
|
||||
import core.game.world.map.RegionManager
|
||||
import org.rsmod.game.pathfinder.LinePathFinder
|
||||
import org.rsmod.game.pathfinder.PathFinder
|
||||
import org.rsmod.game.pathfinder.RayCast
|
||||
import org.rsmod.game.pathfinder.collision.CollisionFlagMap
|
||||
import org.rsmod.game.pathfinder.reach.ReachStrategy
|
||||
import kotlin.math.floor
|
||||
|
||||
private const val SEARCH_MAP_SIZE = 128
|
||||
private const val RING_BUFFER_SIZE = 4096
|
||||
|
||||
class RsmodPathfinder(
|
||||
private val maxWaypoints: Int = 25
|
||||
) : Pathfinder() {
|
||||
|
||||
private val defaultFinder = ThreadLocal.withInitial {
|
||||
PathFinder(RegionManager.RSMOD_CLIPPING_FLAGS, SEARCH_MAP_SIZE, RING_BUFFER_SIZE)
|
||||
}
|
||||
private val suppliedFinder = ThreadLocal.withInitial { RouteFinderState() }
|
||||
|
||||
override fun find(
|
||||
start: Location?,
|
||||
moverSize: Int,
|
||||
dest: Location?,
|
||||
sizeX: Int,
|
||||
sizeY: Int,
|
||||
rotation: Int,
|
||||
type: Int,
|
||||
walkingFlag: Int,
|
||||
near: Boolean,
|
||||
clipMaskSupplier: ClipMaskSupplier?
|
||||
): Path {
|
||||
val source = requireNotNull(start)
|
||||
val destination = requireNotNull(dest)
|
||||
val path = Path()
|
||||
var end = destination
|
||||
val vector = Vector.betweenLocs(source, destination)
|
||||
val magnitude = floor(vector.magnitude())
|
||||
|
||||
if (magnitude > ServerConstants.MAX_PATHFIND_DISTANCE) {
|
||||
if (magnitude < 50.0) {
|
||||
end = source.transform(vector.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1))
|
||||
} else {
|
||||
path.isMoveNear = true
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
val shape = routeShape(type, sizeX, sizeY)
|
||||
val finder = if (clipMaskSupplier == null) {
|
||||
RegionManager.loadClippingWindow(source, SEARCH_MAP_SIZE)
|
||||
defaultFinder.get()
|
||||
} else {
|
||||
val state = suppliedFinder.get()
|
||||
state.loadCollisionWindow(source, clipMaskSupplier)
|
||||
state.finder
|
||||
}
|
||||
val route = finder.findPath(
|
||||
level = source.z,
|
||||
srcX = source.x,
|
||||
srcZ = source.y,
|
||||
destX = end.x,
|
||||
destZ = end.y,
|
||||
srcSize = moverSize,
|
||||
destWidth = if (sizeX == 0) 1 else sizeX,
|
||||
destHeight = if (sizeY == 0) 1 else sizeY,
|
||||
objRot = rotation,
|
||||
objShape = shape,
|
||||
moveNear = near,
|
||||
blockAccessFlags = walkingFlag,
|
||||
maxWaypoints = maxWaypoints
|
||||
)
|
||||
|
||||
if (route.failed) {
|
||||
return path
|
||||
}
|
||||
|
||||
for (waypoint in route.waypoints) {
|
||||
path.points.add(Point(waypoint.x, waypoint.z))
|
||||
}
|
||||
path.setSuccesful(true)
|
||||
path.isMoveNear = route.alternative || end != destination
|
||||
return path
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun canReach(
|
||||
srcX: Int,
|
||||
srcY: Int,
|
||||
moverSize: Int,
|
||||
destX: Int,
|
||||
destY: Int,
|
||||
destWidth: Int,
|
||||
destHeight: Int,
|
||||
rotation: Int,
|
||||
type: Int,
|
||||
walkingFlag: Int,
|
||||
z: Int,
|
||||
clipMaskSupplier: ClipMaskSupplier?
|
||||
): Boolean {
|
||||
val flags = if (clipMaskSupplier == null) {
|
||||
RegionManager.loadClippingWindow(Location.create(srcX, srcY, z), SEARCH_MAP_SIZE)
|
||||
RegionManager.RSMOD_CLIPPING_FLAGS
|
||||
} else {
|
||||
CollisionFlagMap().also {
|
||||
loadCollisionWindow(
|
||||
flags = it,
|
||||
start = Location.create(srcX, srcY, z),
|
||||
supplier = clipMaskSupplier
|
||||
)
|
||||
}
|
||||
}
|
||||
return ReachStrategy.reached(
|
||||
flags = flags,
|
||||
level = z,
|
||||
srcX = srcX,
|
||||
srcZ = srcY,
|
||||
destX = destX,
|
||||
destZ = destY,
|
||||
destWidth = if (destWidth == 0) 1 else destWidth,
|
||||
destHeight = if (destHeight == 0) 1 else destHeight,
|
||||
srcSize = moverSize,
|
||||
objRot = rotation,
|
||||
objShape = routeShape(type, destWidth, destHeight),
|
||||
blockAccessFlags = walkingFlag
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun lineOfSight(
|
||||
start: Location,
|
||||
dest: Location,
|
||||
moverSize: Int,
|
||||
destWidth: Int,
|
||||
destHeight: Int
|
||||
): RayCast {
|
||||
RegionManager.loadClippingWindow(start, SEARCH_MAP_SIZE)
|
||||
return LinePathFinder(RegionManager.RSMOD_PROJECTILE_FLAGS).lineOfSight(
|
||||
level = start.z,
|
||||
srcX = start.x,
|
||||
srcZ = start.y,
|
||||
destX = dest.x,
|
||||
destZ = dest.y,
|
||||
srcSize = moverSize,
|
||||
destWidth = destWidth,
|
||||
destHeight = destHeight
|
||||
)
|
||||
}
|
||||
|
||||
private fun routeShape(type: Int, sizeX: Int, sizeY: Int): Int = when {
|
||||
type >= 0 -> type
|
||||
sizeX != 0 && sizeY != 0 -> 10
|
||||
else -> -1
|
||||
}
|
||||
|
||||
private fun loadCollisionWindow(
|
||||
flags: CollisionFlagMap,
|
||||
start: Location,
|
||||
supplier: ClipMaskSupplier
|
||||
) {
|
||||
val baseX = start.x - (SEARCH_MAP_SIZE / 2)
|
||||
val baseY = start.y - (SEARCH_MAP_SIZE / 2)
|
||||
for (x in baseX until baseX + SEARCH_MAP_SIZE) {
|
||||
for (y in baseY until baseY + SEARCH_MAP_SIZE) {
|
||||
flags[x, y, start.z] = supplier.getClippingFlag(start.z, x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RouteFinderState {
|
||||
val flags = CollisionFlagMap()
|
||||
val finder = PathFinder(flags, SEARCH_MAP_SIZE, RING_BUFFER_SIZE)
|
||||
|
||||
fun loadCollisionWindow(start: Location, supplier: ClipMaskSupplier) {
|
||||
loadCollisionWindow(flags, start, supplier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package core.game.world.map.path
|
||||
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.Point
|
||||
|
||||
class RsmodProjectilePathfinder : Pathfinder() {
|
||||
override fun find(
|
||||
start: Location?,
|
||||
size: Int,
|
||||
end: Location?,
|
||||
sizeX: Int,
|
||||
sizeY: Int,
|
||||
rotation: Int,
|
||||
type: Int,
|
||||
walkingFlag: Int,
|
||||
near: Boolean,
|
||||
clipMaskSupplier: ClipMaskSupplier?
|
||||
): Path {
|
||||
val source = requireNotNull(start)
|
||||
val destination = requireNotNull(end)
|
||||
val rayCast = RsmodPathfinder.lineOfSight(
|
||||
start = source,
|
||||
dest = destination,
|
||||
moverSize = size,
|
||||
destWidth = sizeX,
|
||||
destHeight = sizeY
|
||||
)
|
||||
val path = Path()
|
||||
if (!rayCast.success) {
|
||||
path.isMoveNear = rayCast.alternative
|
||||
return path
|
||||
}
|
||||
for (coordinate in rayCast.coordinates) {
|
||||
path.points.add(Point(coordinate.x, coordinate.z))
|
||||
}
|
||||
path.setSuccesful(true)
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
|
@ -1,598 +0,0 @@
|
|||
package core.game.world.map.path
|
||||
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Direction
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.Point
|
||||
import core.tools.*
|
||||
import core.api.*
|
||||
import core.api.utils.Vector
|
||||
import core.ServerConstants
|
||||
|
||||
import java.util.Comparator
|
||||
import java.util.PriorityQueue
|
||||
|
||||
import java.io.*
|
||||
import javax.imageio.ImageIO
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
class SmartPathfinder
|
||||
/**
|
||||
* Constructs a new `SmartPathfinder` `Object`.
|
||||
*/
|
||||
internal constructor() : Pathfinder() {
|
||||
/**
|
||||
* The x-queue.
|
||||
*/
|
||||
private var queueX: IntArray = intArrayOf(0)
|
||||
|
||||
/**
|
||||
* The y-queue.
|
||||
*/
|
||||
private var queueY: IntArray = intArrayOf(0)
|
||||
|
||||
/**
|
||||
* The "via" array.
|
||||
*/
|
||||
private var via: Array<IntArray> = Array(104) { IntArray(104) }
|
||||
|
||||
/**
|
||||
* The cost array.
|
||||
*/
|
||||
private var cost: Array<IntArray> = Array(104) { IntArray(104) }
|
||||
|
||||
/**
|
||||
* The current writing position.
|
||||
*/
|
||||
private var writePathPosition = 0
|
||||
|
||||
/**
|
||||
* The current x-coordinate.
|
||||
*/
|
||||
private var curX = 0
|
||||
|
||||
/**
|
||||
* The current y-coordinate.
|
||||
*/
|
||||
private var curY = 0
|
||||
|
||||
/**
|
||||
* The destination x-coordinate.
|
||||
*/
|
||||
private var dstX = 0
|
||||
|
||||
/**
|
||||
* The destination y-coordinate.
|
||||
*/
|
||||
private var dstY = 0
|
||||
|
||||
/**
|
||||
* If a path was found.
|
||||
*/
|
||||
private var foundPath = false
|
||||
|
||||
companion object {
|
||||
fun canAttempt(start: Location, dest: Location): Boolean {
|
||||
val distance = kotlin.math.floor(Vector.betweenLocs(start, dest).magnitude())
|
||||
return distance < ServerConstants.MAX_PATHFIND_DISTANCE * 2.0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the pathfinder.
|
||||
*/
|
||||
fun reset() {
|
||||
queueX = IntArray(4096)
|
||||
queueY = IntArray(4096)
|
||||
via = Array(104) { IntArray(104) }
|
||||
cost = Array(104) { IntArray(104) }
|
||||
writePathPosition = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a tile.
|
||||
* @param x The x-coordinate.
|
||||
* @param y The y-coordinate.
|
||||
* @param dir The direction.
|
||||
* @param currentCost The current cost.
|
||||
*/
|
||||
fun check(x: Int, y: Int, dir: Int, currentCost: Int, diagonalPenalty: Int = 0) {
|
||||
if(cost[x][y] > currentCost + diagonalPenalty) {
|
||||
queueX[writePathPosition] = x
|
||||
queueY[writePathPosition] = y
|
||||
via[x][y] = dir
|
||||
cost[x][y] = currentCost + diagonalPenalty
|
||||
writePathPosition = writePathPosition + 1 and 0xfff
|
||||
}
|
||||
}
|
||||
|
||||
override fun find(start: Location?, moverSize: Int, dest: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path {
|
||||
reset()
|
||||
val startLoc = requireNotNull(start)
|
||||
var end = requireNotNull(dest)
|
||||
var vec = Vector.betweenLocs(startLoc, end)
|
||||
var mag = kotlin.math.floor(vec.magnitude())
|
||||
if (mag > ServerConstants.MAX_PATHFIND_DISTANCE) {
|
||||
try {
|
||||
if (canAttempt(startLoc, end)) { //truncate the path if it's realistically long
|
||||
vec = vec.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1)
|
||||
end = startLoc.transform(vec)
|
||||
} else throw Exception("Pathfinding distance exceeds server max! -> " + mag.toString() + " {" + startLoc + "->" + end + "}")
|
||||
} catch (e: Exception) {
|
||||
val sw = StringWriter()
|
||||
val pw = PrintWriter(sw)
|
||||
e.printStackTrace(pw)
|
||||
log(this::class.java, Log.FINE, sw.toString())
|
||||
val p = Path()
|
||||
p.isMoveNear = true
|
||||
return p
|
||||
}
|
||||
}
|
||||
val path = Path()
|
||||
foundPath = false
|
||||
for (x in 0..103) {
|
||||
for (y in 0..103) {
|
||||
via[x][y] = 0
|
||||
cost[x][y] = 99999999
|
||||
}
|
||||
}
|
||||
val z = startLoc.z
|
||||
val location = Location.create(startLoc.regionX - 6 shl 3, startLoc.regionY - 6 shl 3, z)
|
||||
curX = startLoc.sceneX
|
||||
curY = startLoc.sceneY
|
||||
dstX = end.getSceneX(startLoc)
|
||||
dstY = end.getSceneY(startLoc)
|
||||
var attempts: Int
|
||||
var readPosition: Int
|
||||
check(curX, curY, 99, 0)
|
||||
try {
|
||||
if (moverSize < 2) {
|
||||
if(GameWorld.settings?.smartpathfinder_bfs ?: false) {
|
||||
checkSingleTraversal(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!)
|
||||
} else {
|
||||
checkSingleTraversalAstar(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!)
|
||||
}
|
||||
} else if (moverSize == 2) {
|
||||
checkDoubleTraversal(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!)
|
||||
} else {
|
||||
checkVariableTraversal(end, moverSize, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!)
|
||||
}
|
||||
} catch (e: Exception) {}
|
||||
var debugImg = if(false) { BufferedImage(4*104+2, 104, BufferedImage.TYPE_INT_RGB) } else { null }
|
||||
if(debugImg != null) {
|
||||
for(y in 0 until 104) {
|
||||
for(x in 0 until 104) {
|
||||
debugImg.setRGB(x, 103-y, via[x][y] * (((1 shl 24)-1)/12))
|
||||
val c = Math.min(4*Math.min(cost[x][y], 64), 255)
|
||||
debugImg.setRGB(105+x, 103-y, (c shl 16) or (c shl 8) or c)
|
||||
debugImg.setRGB(2*105+x, 103-y, clipMaskSupplier!!.getClippingFlag(location.z, location.x + x, location.y + y))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundPath) {
|
||||
if (near) {
|
||||
var fullCost = 1000
|
||||
var thisCost = 100
|
||||
val depth = 10
|
||||
for (x in dstX - depth..dstX + depth) {
|
||||
for (y in dstY - depth..dstY + depth) {
|
||||
if (x >= 0 && y >= 0 && x < 104 && y < 104 && cost[x][y] < 100) {
|
||||
var diffX = 0
|
||||
if (x < dstX) {
|
||||
diffX = dstX - x
|
||||
} else if (x > dstX + sizeX - 1) {
|
||||
diffX = x - (dstX + sizeX - 1)
|
||||
}
|
||||
var diffY = 0
|
||||
if (y < dstY) {
|
||||
diffY = dstY - y
|
||||
} else if (y > dstY + sizeY - 1) {
|
||||
diffY = y - (dstY + sizeY - 1)
|
||||
}
|
||||
val totalCost = diffX * diffX + diffY * diffY
|
||||
if (totalCost < fullCost || totalCost == fullCost && cost[x][y] < thisCost) {
|
||||
fullCost = totalCost
|
||||
thisCost = cost[x][y]
|
||||
curX = x
|
||||
curY = y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fullCost == 1000) {
|
||||
return path
|
||||
}
|
||||
path.isMoveNear = true
|
||||
}
|
||||
}
|
||||
readPosition = 0
|
||||
queueX[readPosition] = curX
|
||||
queueY[readPosition++] = curY
|
||||
var previousDirection: Int
|
||||
attempts = 0
|
||||
var directionFlag = via[curX][curY].also { previousDirection = it }
|
||||
while (curX != start.sceneX || curY != start.sceneY) {
|
||||
if (++attempts > queueX.size) {
|
||||
return path
|
||||
}
|
||||
previousDirection = directionFlag
|
||||
queueX[readPosition] = curX
|
||||
queueY[readPosition++] = curY
|
||||
if (directionFlag and WEST_FLAG != 0) {
|
||||
curX++
|
||||
} else if (directionFlag and EAST_FLAG != 0) {
|
||||
curX--
|
||||
}
|
||||
if (directionFlag and SOUTH_FLAG != 0) {
|
||||
curY++
|
||||
} else if (directionFlag and NORTH_FLAG != 0) {
|
||||
curY--
|
||||
}
|
||||
if(debugImg != null) {
|
||||
debugImg.setRGB(3*105+curX, 103-curY, 0x0000ff)
|
||||
}
|
||||
directionFlag = via[curX][curY]
|
||||
}
|
||||
if(debugImg != null) {
|
||||
debugImg.setRGB(3*105+start.sceneX, 103-start.sceneY, 0xff0000)
|
||||
debugImg.setRGB(3*105+dstX, 103-dstY, 0x00ff00)
|
||||
if(GameWorld.settings?.smartpathfinder_bfs ?: false) {
|
||||
ImageIO.write(debugImg, "png", File(String.format("bfs_%04d_%04d_%04d_%04d.png", start.x, start.y, end.x, end.y)))
|
||||
} else {
|
||||
ImageIO.write(debugImg, "png", File(String.format("astar_%04d_%04d_%04d_%04d.png", start.x, start.y, end.x, end.y)))
|
||||
}
|
||||
}
|
||||
val size = readPosition--
|
||||
var absX = location.x + queueX[readPosition]
|
||||
var absY = location.y + queueY[readPosition]
|
||||
path.points.add(Point(absX, absY))
|
||||
for (i in 1 until size) {
|
||||
readPosition--
|
||||
absX = location.x + queueX[readPosition]
|
||||
absY = location.y + queueY[readPosition]
|
||||
path.points.add(Point(absX, absY))
|
||||
}
|
||||
path.setSuccesful(true)
|
||||
if (end != dest)
|
||||
path.isMoveNear = true
|
||||
return path
|
||||
}
|
||||
|
||||
class UIntAsPointComparator(val end: Location) : Comparator<UInt> {
|
||||
override fun compare(p: UInt, q: UInt): Int {
|
||||
val pc: UInt = (p and 0x00ff0000u) shr 16
|
||||
val px: UInt = (p and 0x0000ff00u) shr 8
|
||||
val py: UInt = (p and 0x000000ffu)
|
||||
val qc: UInt = (q and 0x00ff0000u) shr 16
|
||||
val qx: UInt = (q and 0x0000ff00u) shr 8
|
||||
val qy: UInt = (q and 0x000000ffu)
|
||||
//val dp = pc.toInt() + Math.abs(end.sceneX - (px.toInt())) + Math.abs(end.sceneY - (py.toInt()))
|
||||
//val dq = qc.toInt() + Math.abs(end.sceneX - (qx.toInt())) + Math.abs(end.sceneY - (qy.toInt()))
|
||||
val dp = pc.toDouble() + Math.max(Math.abs(end.sceneX - px.toInt()), Math.abs(end.sceneY - py.toInt())).toDouble()
|
||||
val dq = qc.toDouble() + Math.max(Math.abs(end.sceneX - qx.toInt()), Math.abs(end.sceneY - qy.toInt())).toDouble()
|
||||
if(dp < dq) {
|
||||
return -1
|
||||
} else if(dq < dp) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other is UIntAsPointComparator) {
|
||||
return end == other.end
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
override fun hashCode(): Int {
|
||||
return end.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSingleTraversalAstar(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) {
|
||||
val z = location.z
|
||||
var queue = PriorityQueue(4096, UIntAsPointComparator(end))
|
||||
queue.add(((curX.toUInt()) shl 8) or (curY.toUInt()))
|
||||
while(!foundPath && !queue.isEmpty()) {
|
||||
val point = queue.poll()
|
||||
val curCost = ((point and 0xff0000u) shr 16).toInt()
|
||||
curX = ((point and 0x0000ff00u) shr 8).toInt()
|
||||
curY = (point and 0x000000ffu).toInt()
|
||||
val absX = location.x + curX
|
||||
val absY = location.y + curY
|
||||
if (curX == dstX && curY == dstY) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
if (type != 0) {
|
||||
if ((type < 5 || type == 10) && canDoorInteract(absX, absY, 1, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
if (type < 10 && canDecorationInteract(absX, absY, 1, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, 1, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
val newCost = curCost + 1
|
||||
//val orthogonalsFirst = arrayOf(Direction.EAST, Direction.NORTH, Direction.WEST, Direction.SOUTH, Direction.NORTH_EAST, Direction.NORTH_WEST, Direction.SOUTH_WEST, Direction.SOUTH_EAST)
|
||||
val orthogonalsFirst = arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST, Direction.SOUTH_WEST, Direction.NORTH_WEST, Direction.SOUTH_EAST, Direction.NORTH_EAST)
|
||||
//val orthogonalsFirst = arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST)
|
||||
//for(dir in Direction.values()) {
|
||||
for(dir in orthogonalsFirst) {
|
||||
val newSceneX: Int = curX + dir.stepX
|
||||
val newSceneY: Int = curY + dir.stepY
|
||||
if(0 <= newSceneX && newSceneX < 104 && 0 <= newSceneY && newSceneY < 104 && via[newSceneX][newSceneY] == 0) {
|
||||
if(dir.canMoveFrom(z, absX, absY, clipMaskSupplier)) {
|
||||
val diagonalPenalty = Math.abs(dir.stepX) + Math.abs(dir.stepY) - 1
|
||||
val flag = flagForDirection(dir)
|
||||
check(newSceneX, newSceneY, flag, newCost, diagonalPenalty)
|
||||
if(via[newSceneX][newSceneY] == flag) {
|
||||
queue.add(((newCost + diagonalPenalty).toUInt() shl 16) or (newSceneX.toUInt() shl 8) or newSceneY.toUInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks possible traversal for a size 1 entity.
|
||||
* @param end The destination location.
|
||||
* @param sizeX The x-size of the destination.
|
||||
* @param sizeY The y-size of the destination.
|
||||
* @param type The object type.
|
||||
* @param rotation The object rotation.
|
||||
* @param walkingFlag The walking flag.
|
||||
* @param location The viewport location.
|
||||
*/
|
||||
private fun checkSingleTraversal(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) {
|
||||
var readPosition = 0
|
||||
val z = location.z
|
||||
while (writePathPosition != readPosition) {
|
||||
curX = queueX[readPosition]
|
||||
curY = queueY[readPosition]
|
||||
readPosition = readPosition + 1 and 0xfff
|
||||
if (curX == dstX && curY == dstY) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
try {
|
||||
val absX = location.x + curX
|
||||
val absY = location.y + curY
|
||||
if (type != 0) {
|
||||
if ((type < 5 || type == 10) && canDoorInteract(absX, absY, 1, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
if (type < 10 && canDecorationInteract(absX, absY, 1, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, 1, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
val thisCost = cost[curX][curY] + 1
|
||||
if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) {
|
||||
check(curX, curY - 1, SOUTH_FLAG, thisCost)
|
||||
}
|
||||
if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0) {
|
||||
check(curX - 1, curY, WEST_FLAG, thisCost)
|
||||
}
|
||||
if (curY < 103 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) {
|
||||
check(curX, curY + 1, NORTH_FLAG, thisCost)
|
||||
}
|
||||
if (curX < 103 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0) {
|
||||
check(curX + 1, curY, EAST_FLAG, thisCost)
|
||||
}
|
||||
if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) {
|
||||
check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost)
|
||||
}
|
||||
if (curX > 0 && curY < 103 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) {
|
||||
check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost)
|
||||
}
|
||||
if (curX < 103 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) {
|
||||
check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost)
|
||||
}
|
||||
if (curX < 103 && curY < 103 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 1) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) {
|
||||
check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// e.printStackTrace()println("curX " + curX + " curY" + curY + " via " + via[curX + 1] + via[curY + 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks possible traversal for a size 2 entity.
|
||||
* @param end The destination location.
|
||||
* @param sizeX The x-size of the destination.
|
||||
* @param sizeY The y-size of the destination.
|
||||
* @param type The object type.
|
||||
* @param rotation The object rotation.
|
||||
* @param walkingFlag The walking flag.
|
||||
* @param location The viewport location.
|
||||
*/
|
||||
private fun checkDoubleTraversal(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) {
|
||||
var readPosition = 0
|
||||
val z = location.z
|
||||
while (writePathPosition != readPosition) {
|
||||
curX = queueX[readPosition]
|
||||
curY = queueY[readPosition]
|
||||
readPosition = readPosition + 1 and 0xfff
|
||||
if (curX == dstX && curY == dstY) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
val absX = location.x + curX
|
||||
val absY = location.y + curY
|
||||
if (type != 0) {
|
||||
if ((type < 5 || type == 10) && canDoorInteract(absX, absY, 2, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
if (type < 10 && canDecorationInteract(absX, absY, 2, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, 2, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
val thisCost = cost[curX][curY] + 1
|
||||
if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c0183 == 0) {
|
||||
check(curX, curY - 1, SOUTH_FLAG, thisCost)
|
||||
}
|
||||
if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c0138 == 0) {
|
||||
check(curX - 1, curY, WEST_FLAG, thisCost)
|
||||
}
|
||||
if (curY < 102 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 2) and 0x12c01e0 == 0) {
|
||||
check(curX, curY + 1, NORTH_FLAG, thisCost)
|
||||
}
|
||||
if (curX < 102 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 1) and 0x12c01e0 == 0) {
|
||||
check(curX + 1, curY, EAST_FLAG, thisCost)
|
||||
}
|
||||
if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0183 == 0) {
|
||||
check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost)
|
||||
}
|
||||
if (curX > 0 && curY < 102 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 2) and 0x12c01e0 == 0) {
|
||||
check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost)
|
||||
}
|
||||
if (curX < 102 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY - 1) and 0x12c0183 == 0) {
|
||||
check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost)
|
||||
}
|
||||
if (curX < 102 && curY < 102 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 2) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 1) and 0x12c0183 == 0) {
|
||||
check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks possible traversal for any sized entity.
|
||||
* @param end The destination location.
|
||||
* @param size The mover size.
|
||||
* @param sizeX The x-size of the destination.
|
||||
* @param sizeY The y-size of the destination.
|
||||
* @param type The object type.
|
||||
* @param rotation The object rotation.
|
||||
* @param walkingFlag The walking flag.
|
||||
* @param location The viewport location.
|
||||
*/
|
||||
private fun checkVariableTraversal(end: Location, size: Int, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) {
|
||||
var readPosition = 0
|
||||
val z = location.z
|
||||
main@ while (writePathPosition != readPosition) {
|
||||
curX = queueX[readPosition]
|
||||
curY = queueY[readPosition]
|
||||
readPosition = readPosition + 1 and 0xfff
|
||||
if (curX == dstX && curY == dstY) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
val absX = location.x + curX
|
||||
val absY = location.y + curY
|
||||
if (type != 0) {
|
||||
if ((type < 5 || type == 10) && canDoorInteract(absX, absY, size, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
if (type < 10 && canDecorationInteract(absX, absY, size, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, size, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) {
|
||||
foundPath = true
|
||||
break
|
||||
}
|
||||
val thisCost = cost[curX][curY] + 1
|
||||
south@ do {
|
||||
if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 1), absY - 1) and 0x12c0183 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX + i, absY - 1) and 0x12c018f != 0) {
|
||||
break@south
|
||||
}
|
||||
}
|
||||
check(curX, curY - 1, SOUTH_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
west@ do {
|
||||
if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (size - 1)) and 0x12c0138 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + i) and 0x12c013e != 0) {
|
||||
break@west
|
||||
}
|
||||
}
|
||||
check(curX - 1, curY, WEST_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
north@ do {
|
||||
if (curY < 102 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 1), absY + size) and 0x12c01e0 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX + i, absY + size) and 0x12c01f8 != 0) {
|
||||
break@north
|
||||
}
|
||||
}
|
||||
check(curX, curY + 1, NORTH_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
east@ do {
|
||||
if (curX < 102 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + (size - 1)) and 0x12c01e0 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX + size, absY + i) and 0x12c01e3 != 0) {
|
||||
break@east
|
||||
}
|
||||
}
|
||||
check(curX + 1, curY, EAST_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
southWest@ do {
|
||||
if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (size - 2)) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 2), absY - 1) and 0x12c0183 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (i - 1)) and 0x12c013e != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i - 1), absY - 1) and 0x12c018f != 0) {
|
||||
break@southWest
|
||||
}
|
||||
}
|
||||
check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
northWest@ do {
|
||||
if (curX > 0 && curY < 102 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + size) and 0x12c01e0 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (i + 1)) and 0x12c013e != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i - 1), absY + size) and 0x12c01f8 != 0) {
|
||||
break@northWest
|
||||
}
|
||||
}
|
||||
check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
southEast@ do {
|
||||
if (curX < 102 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY - 1) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + (size - 2)) and 0x12c01e0 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX + size, absY + (i - 1)) and 0x12c01e3 != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i + 1), absY - 1) and 0x12c018f != 0) {
|
||||
break@southEast
|
||||
}
|
||||
}
|
||||
check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost)
|
||||
}
|
||||
} while (false)
|
||||
if (curX < 102 && curY < 102 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + size) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + 1) and 0x12c0183 == 0) {
|
||||
for (i in 1 until size - 1) {
|
||||
if (clipMaskSupplier.getClippingFlag(z, absX + (i + 1), absY + size) and 0x12c01f8 != 0 || clipMaskSupplier.getClippingFlag(z, absX + size, absY + (i + 1)) and 0x12c01e3 != 0) {
|
||||
continue@main
|
||||
}
|
||||
}
|
||||
check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -296,7 +296,9 @@ class EasterEvent : WorldEvent("easter"), TickListener, InteractionListener, Log
|
|||
val dir = dirs[RandomFunction.random(dirs.size)]
|
||||
var loc = player.location.transform(dir, 3)
|
||||
val path = Pathfinder.find(player, loc)
|
||||
loc = Location.create(path.points.last.x, path.points.last.y, loc.z)
|
||||
path.points.lastOrNull()?.let {
|
||||
loc = Location.create(it.x, it.y, loc.z)
|
||||
}
|
||||
GroundItemManager.create(Item(eggs.random()), loc, player)
|
||||
sendMessage(player, colorize("%RAn egg has appeared nearby."))
|
||||
}
|
||||
|
|
@ -317,4 +319,4 @@ class EasterEvent : WorldEvent("easter"), TickListener, InteractionListener, Log
|
|||
WeightedItem(Items.DRAGON_IMPLING_JAR_11256, 1, 1, 0.005)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package core
|
||||
|
||||
import TestUtils
|
||||
import content.global.handlers.scenery.BankBoothListener
|
||||
import content.global.skill.gather.GatheringSkillOptionListeners
|
||||
import content.global.skill.gather.woodcutting.WoodcuttingListener
|
||||
import core.api.log
|
||||
|
|
@ -17,21 +18,24 @@ import core.game.node.entity.npc.NPC
|
|||
import core.game.node.entity.player.Player
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Region
|
||||
import core.game.world.map.path.SmartPathfinder
|
||||
import core.game.world.map.path.ClipMaskSupplier
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.net.packet.PacketProcessor
|
||||
import core.plugin.ClassScanner
|
||||
import core.plugin.Plugin
|
||||
import core.tools.Log
|
||||
import org.rs09.consts.NPCs
|
||||
import org.rs09.consts.Scenery as SceneryIds
|
||||
|
||||
class PathfinderTests {
|
||||
companion object {init {TestUtils.preTestSetup(); GatheringSkillOptionListeners().defineListeners(); WoodcuttingListener().defineListeners() }; val NPC_TEST_LOC = ServerConstants.HOME_LOCATION!!.transform(2, 10, 0)}
|
||||
|
||||
@Test fun smartPathfinderShouldRejectDestinationsAtTheTruncationLimit() {
|
||||
val start = Location.create(3165, 3218, 0)
|
||||
|
||||
Assertions.assertTrue(SmartPathfinder.canAttempt(start, Location.create(3203, 3186, 0)))
|
||||
Assertions.assertFalse(SmartPathfinder.canAttempt(start, Location.create(3203, 3185, 0)))
|
||||
companion object {
|
||||
init {
|
||||
TestUtils.preTestSetup()
|
||||
GatheringSkillOptionListeners().defineListeners()
|
||||
WoodcuttingListener().defineListeners()
|
||||
BankBoothListener().defineListeners()
|
||||
}
|
||||
val NPC_TEST_LOC = ServerConstants.HOME_LOCATION!!.transform(2, 10, 0)
|
||||
}
|
||||
|
||||
@Test fun getOccupiedTilesShouldReturnCorrectSetOfTilesThatAnObjectOccupiesAtAllRotations() {
|
||||
|
|
@ -55,6 +59,166 @@ class PathfinderTests {
|
|||
Assertions.assertArrayEquals(arrayOf(Location.create(50,50), Location.create(49,50)), occupiedAt3)
|
||||
}
|
||||
|
||||
@Test fun smartPathfinderShouldRespectSuppliedClipMask() {
|
||||
val start = Location.create(3200, 3200, 0)
|
||||
val dest = Location.create(3202, 3200, 0)
|
||||
val blockedDestination = ClipMaskSupplier { _, x, y ->
|
||||
if (x == dest.x && y == dest.y) 0x100 else 0
|
||||
}
|
||||
|
||||
val path = Pathfinder.SMART.find(start, 1, dest, 1, 1, 0, 0, 0, true, blockedDestination)
|
||||
|
||||
Assertions.assertTrue(path.isSuccessful)
|
||||
Assertions.assertEquals(
|
||||
Location.create(3201, 3200, 0),
|
||||
Location.create(path.points.last.x, path.points.last.y, 0)
|
||||
)
|
||||
}
|
||||
|
||||
@Test fun dumbPathfinderShouldUseRsmodRouting() {
|
||||
val start = Location.create(3200, 3200, 0)
|
||||
val dest = Location.create(3202, 3200, 0)
|
||||
val blockedMiddle = ClipMaskSupplier { _, x, y ->
|
||||
if (x == 3201 && y == 3200) 0x100 else 0
|
||||
}
|
||||
|
||||
val path = Pathfinder.DUMB.find(start, 1, dest, 0, 0, 0, -1, 0, false, blockedMiddle)
|
||||
|
||||
Assertions.assertTrue(path.isSuccessful)
|
||||
Assertions.assertTrue(path.points.isNotEmpty())
|
||||
}
|
||||
|
||||
@Test fun projectilePathfinderShouldUseRsmodLineOfSightFlags() {
|
||||
val start = Location.create(3200, 3200, 0)
|
||||
val dest = Location.create(3202, 3200, 0)
|
||||
RegionManager.loadClippingWindow(start, 128)
|
||||
try {
|
||||
RegionManager.setRsmodFlag(0, 3201, 3200, true, 0x20000)
|
||||
|
||||
val blocked = Pathfinder.PROJECTILE.find(start, 1, dest, 0, 0, 0, -1, 0, false, null)
|
||||
|
||||
Assertions.assertFalse(blocked.isSuccessful)
|
||||
} finally {
|
||||
RegionManager.setRsmodFlag(0, 3201, 3200, true, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun metadataSceneryInteractionShouldTriggerWhenAlreadyAtRsmodApproachTile() {
|
||||
TestUtils.getMockPlayer("bankBoothApproach").use { p ->
|
||||
val (booth, approach) = findReachableBankBoothFixture()
|
||||
p.location = approach
|
||||
val alreadyAtPath = Pathfinder.find(p, booth)
|
||||
Assertions.assertTrue(alreadyAtPath.isSuccessful)
|
||||
Assertions.assertFalse(alreadyAtPath.isMoveNear)
|
||||
|
||||
Assertions.assertTrue(InteractionListeners.run(booth.id, IntType.SCENERY, "bank", p, booth))
|
||||
TestUtils.advanceTicks(10, false)
|
||||
|
||||
Assertions.assertTrue(p.bank.isOpen)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun metadataSceneryCollectShouldTriggerWhenAlreadyAtRsmodApproachTile() {
|
||||
TestUtils.getMockPlayer("bankBoothCollectApproach").use { p ->
|
||||
val (booth, approach) = findReachableBankBoothFixture()
|
||||
p.location = approach
|
||||
var collected = false
|
||||
InteractionListeners.addMetadata(
|
||||
booth.id,
|
||||
IntType.SCENERY,
|
||||
arrayOf("collect"),
|
||||
InteractionListener.InteractionMetadata({ _, _, _ ->
|
||||
collected = true
|
||||
true
|
||||
}, 1, false)
|
||||
)
|
||||
|
||||
try {
|
||||
Assertions.assertTrue(InteractionListeners.run(booth.id, IntType.SCENERY, "collect", p, booth))
|
||||
TestUtils.advanceTicks(10, false)
|
||||
|
||||
Assertions.assertTrue(collected)
|
||||
} finally {
|
||||
BankBoothListener().defineListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun directObjectMovementPulseShouldTriggerWhenAlreadyAtRsmodApproachTile() {
|
||||
TestUtils.getMockPlayer("objectPulseApproach").use { p ->
|
||||
val tree = RegionManager.getObject(0, 2720, 3475, 1307)
|
||||
?: throw AssertionError("Expected test tree object.")
|
||||
val approach = findReachableApproachTile(tree)
|
||||
var pulsed = false
|
||||
p.location = approach
|
||||
|
||||
GameWorld.Pulser.submit(object : MovementPulse(p, tree) {
|
||||
override fun pulse(): Boolean {
|
||||
pulsed = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
TestUtils.advanceTicks(3, false)
|
||||
|
||||
Assertions.assertTrue(pulsed)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun entityMovementPulseShouldTriggerWhenDestinationOverrideIsAlreadyReached() {
|
||||
TestUtils.getMockPlayer("bankerOverrideApproach").use { p ->
|
||||
val npc = NPC.create(0, NPC_TEST_LOC)
|
||||
npc.isNeverWalks = true
|
||||
npc.init()
|
||||
p.location = ServerConstants.HOME_LOCATION
|
||||
var pulsed = false
|
||||
|
||||
GameWorld.Pulser.submit(object : MovementPulse(p, npc, DestinationFlag.ENTITY, { _, _ -> p.location }) {
|
||||
override fun pulse(): Boolean {
|
||||
pulsed = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
TestUtils.advanceTicks(3, false)
|
||||
|
||||
Assertions.assertTrue(pulsed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findReachableBankBoothFixture(): Pair<Scenery, Location> {
|
||||
val base = ServerConstants.HOME_LOCATION!!.transform(8, 8, 0)
|
||||
for (rotation in 0..3) {
|
||||
val booth = Scenery(SceneryIds.BANK_BOOTH_2213, base, 10, rotation)
|
||||
runCatching { findReachableApproachTile(booth) }
|
||||
.getOrNull()
|
||||
?.let { return booth to it }
|
||||
}
|
||||
throw AssertionError("Could not find a reachable synthetic bank booth fixture.")
|
||||
}
|
||||
|
||||
private fun findReachableApproachTile(scenery: Scenery): Location {
|
||||
for (radius in 1..8) {
|
||||
for (x in scenery.location.x - radius..scenery.location.x + radius) {
|
||||
for (y in scenery.location.y - radius..scenery.location.y + radius) {
|
||||
val start = Location.create(x, y, scenery.location.z)
|
||||
if (!RegionManager.isTeleportPermitted(start)) {
|
||||
continue
|
||||
}
|
||||
val path = Pathfinder.find(start, scenery)
|
||||
if (!path.isSuccessful || path.isMoveNear) {
|
||||
continue
|
||||
}
|
||||
val point = path.points.lastOrNull()
|
||||
val approach = Location.create(point?.x ?: start.x, point?.y ?: start.y, start.z)
|
||||
val check = Pathfinder.find(approach, scenery)
|
||||
if (check.isSuccessful && !check.isMoveNear) {
|
||||
return approach
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw AssertionError("Could not find a reachable approach tile for $scenery.")
|
||||
}
|
||||
|
||||
@Test fun movementPulseShouldStopEarlyIfNextToATileOccupiedByTargetObject() {
|
||||
val start = Location.create(2731, 3481)
|
||||
val dest = RegionManager.getObject(0, 2720, 3475, 1307)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue