diff --git a/Server/pom.xml b/Server/pom.xml
index 5c93c44d2..71fad5580 100644
--- a/Server/pom.xml
+++ b/Server/pom.xml
@@ -63,6 +63,12 @@
[1.4.0,)
compile
+
+ org.rsmod
+ rsmod-pathfinder
+ 4.2.1
+ compile
+
mysql
mysql-connector-java
diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java
index b378a8a1f..220e2d5dc 100644
--- a/Server/src/main/core/game/interaction/MovementPulse.java
+++ b/Server/src/main/core/game/interaction/MovementPulse.java
@@ -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;
}
diff --git a/Server/src/main/core/game/interaction/ScriptProcessor.kt b/Server/src/main/core/game/interaction/ScriptProcessor.kt
index 2c638f35f..8c09165a1 100644
--- a/Server/src/main/core/game/interaction/ScriptProcessor.kt
+++ b/Server/src/main/core/game/interaction/ScriptProcessor.kt
@@ -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
diff --git a/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt b/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt
index e4fa6676b..cc54b0ab0 100644
--- a/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt
+++ b/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt
@@ -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
}
/**
diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt
index 58e3e6ebf..27efac098 100644
--- a/Server/src/main/core/game/system/config/ServerConfigParser.kt
+++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt
@@ -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)
diff --git a/Server/src/main/core/game/world/GameSettings.kt b/Server/src/main/core/game/world/GameSettings.kt
index e113dbbe3..806977653 100644
--- a/Server/src/main/core/game/world/GameSettings.kt
+++ b/Server/src/main/core/game/world/GameSettings.kt
@@ -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
diff --git a/Server/src/main/core/game/world/map/RegionManager.kt b/Server/src/main/core/game/world/map/RegionManager.kt
index 9fb7b848b..d70f5c0d6 100644
--- a/Server/src/main/core/game/world/map/RegionManager.kt
+++ b/Server/src/main/core/game/world/map/RegionManager.kt
@@ -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 = HashMap()
+ private val RSMOD_PROJECTILE_REGIONS = HashSet()
@JvmStatic val CLIPPING_FLAGS = HashMap>()
@JvmStatic val PROJECTILE_FLAGS = HashMap>()
+ @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 {
- 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)
+ }
+ }
+ }
}
/**
diff --git a/Server/src/main/core/game/world/map/build/RegionFlags.java b/Server/src/main/core/game/world/map/build/RegionFlags.java
index 177da33c9..671935213 100644
--- a/Server/src/main/core/game/world/map/build/RegionFlags.java
+++ b/Server/src/main/core/game/world/map/build/RegionFlags.java
@@ -179,7 +179,9 @@ public final class RegionFlags {
public void addFlag(int x, int y, int clipdata) {
int current = getFlag(x, y);
Pair 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 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 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;
}
-}
\ No newline at end of file
+}
diff --git a/Server/src/main/core/game/world/map/path/DumbPathfinder.java b/Server/src/main/core/game/world/map/path/DumbPathfinder.java
deleted file mode 100644
index 8bcfb32e0..000000000
--- a/Server/src/main/core/game/world/map/path/DumbPathfinder.java
+++ /dev/null
@@ -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..
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 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 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 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 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];
- }
-
-}
diff --git a/Server/src/main/core/game/world/map/path/Pathfinder.java b/Server/src/main/core/game/world/map/path/Pathfinder.java
index 9fce3a782..50dca754f 100644
--- a/Server/src/main/core/game/world/map/path/Pathfinder.java
+++ b/Server/src/main/core/game/world/map/path/Pathfinder.java
@@ -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);
}
}
diff --git a/Server/src/main/core/game/world/map/path/ProjectilePathfinder.java b/Server/src/main/core/game/world/map/path/ProjectilePathfinder.java
deleted file mode 100644
index d20b96c22..000000000
--- a/Server/src/main/core/game/world/map/path/ProjectilePathfinder.java
+++ /dev/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 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 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];
- }
-}
\ No newline at end of file
diff --git a/Server/src/main/core/game/world/map/path/RsmodPathfinder.kt b/Server/src/main/core/game/world/map/path/RsmodPathfinder.kt
new file mode 100644
index 000000000..b6409eb6c
--- /dev/null
+++ b/Server/src/main/core/game/world/map/path/RsmodPathfinder.kt
@@ -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)
+ }
+ }
+}
diff --git a/Server/src/main/core/game/world/map/path/RsmodProjectilePathfinder.kt b/Server/src/main/core/game/world/map/path/RsmodProjectilePathfinder.kt
new file mode 100644
index 000000000..1169f875d
--- /dev/null
+++ b/Server/src/main/core/game/world/map/path/RsmodProjectilePathfinder.kt
@@ -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
+ }
+}
diff --git a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt
deleted file mode 100644
index 1087d318b..000000000
--- a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt
+++ /dev/null
@@ -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 = Array(104) { IntArray(104) }
-
- /**
- * The cost array.
- */
- private var cost: Array = 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 {
- 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)
- }
- }
- }
-}
diff --git a/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt
index 6b129b906..7dcae1c12 100644
--- a/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt
+++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt
@@ -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)
)
}
-}
\ No newline at end of file
+}
diff --git a/Server/src/test/kotlin/core/PathfinderTests.kt b/Server/src/test/kotlin/core/PathfinderTests.kt
index c30c69402..fb6f31ac6 100644
--- a/Server/src/test/kotlin/core/PathfinderTests.kt
+++ b/Server/src/test/kotlin/core/PathfinderTests.kt
@@ -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 {
+ 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)