mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-01 14:39:13 -06:00
Modernised some location/pathing architecture
This commit is contained in:
parent
afeffc1b98
commit
5822b8df99
12 changed files with 275 additions and 596 deletions
|
|
@ -193,7 +193,7 @@ enum class EnchantedJewellery(
|
|||
),
|
||||
RING_OF_LIFE(arrayOf<String>(),
|
||||
arrayOf(
|
||||
Location.create(ServerConstants.HOME_LOCATION)
|
||||
Location.create(ServerConstants.HOME_LOCATION!!)
|
||||
),
|
||||
true,
|
||||
Items.RING_OF_LIFE_2570
|
||||
|
|
|
|||
|
|
@ -129,9 +129,9 @@ class SheepBehavior : NPCBehavior(*sheepIds), InteractionListener {
|
|||
val sheepDirection = Direction.getDirection(sheepLocation, playerLocation) // Get direction sheep is facing, from the player's location
|
||||
val sheepOppositeDirection = sheepDirection.getOpposite() // Switch to opposite direction
|
||||
|
||||
val xWalkLocation = sheepLocation.getX() + (sheepOppositeDirection.getStepX() * 3) // Gets x location, if set, 3 steps away from player
|
||||
val yWalkLocation = sheepLocation.getY() + (sheepOppositeDirection.getStepY() * 3) // Gets y location, if set, 3 steps away from player
|
||||
val sheepWalkToLocation = Location(xWalkLocation, yWalkLocation, sheepLocation.getZ()); // New location for pathfinding
|
||||
val xWalkLocation = sheepLocation.x + (sheepOppositeDirection.getStepX() * 3) // Gets x location, if set, 3 steps away from player
|
||||
val yWalkLocation = sheepLocation.y + (sheepOppositeDirection.getStepY() * 3) // Gets y location, if set, 3 steps away from player
|
||||
val sheepWalkToLocation = Location(xWalkLocation, yWalkLocation, sheepLocation.z); // New location for pathfinding
|
||||
|
||||
sendMessage(player, messagePlayer)
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class LightSourceLighter : UseWithHandler(590,36,38){
|
|||
// For Temple of Ikov - if you are in the dark basement and light a light source, switch to the light basement.
|
||||
// For the listener that covers the firemaking cape perk, see content.global.skill.skillcapeperks.SkillcapePerks.kt
|
||||
if(event.player.location.isInRegion(10648) && event.player.location.withinDistance(Location(2639,9738,0), 8)) {
|
||||
teleport(event.player, Location.create(event.player.getLocation().getX(), event.player.getLocation().getY() + 23, event.player.getLocation().getZ()))
|
||||
teleport(event.player, Location.create(event.player.getLocation().x, event.player.getLocation().y + 23, event.player.getLocation().z))
|
||||
closeDialogue(event.player)
|
||||
// Dark basement is region 10648, min 2639 9738 0, max 2643 9744 0. Add 23 to the Y loc to tele to light basement
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ class WoodcuttingListener : InteractionListener {
|
|||
var amount = amount
|
||||
var experience: Double = resource.getExperience()
|
||||
val reward = resource.reward
|
||||
if (player.getLocation().getRegionId() == 10300) {
|
||||
if (player.getLocation().regionId == 10300) {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)?
|
|||
// For Temple of Ikov - if you are in the dark basement and put on the firemaking cape with its 2009Scape light source perk, switch to the light basement.
|
||||
// For the listener that teleports if you light a normal light source, see content.global.skill.crafting.lightsources.LightSourceLighter.kt
|
||||
if(player.location.isInRegion(10648) && player.location.withinDistance(Location(2639,9738,0), 8)) {
|
||||
teleport(player, Location.create(player.getLocation().getX(), player.getLocation().getY() + 23, player.getLocation().getZ()))
|
||||
teleport(player, Location.create(player.getLocation().x, player.getLocation().y + 23, player.getLocation().z))
|
||||
closeDialogue(player)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ class DraynorManorHouseZone : MapZone("Draynor Manor House", true), Plugin<Any?>
|
|||
if (n.shouldPreventStacking(e)) {
|
||||
val s1 = e.size()
|
||||
val s2 = n.size()
|
||||
val x = destination.getX()
|
||||
val y = destination.getY()
|
||||
val x = destination.x
|
||||
val y = destination.y
|
||||
val l = n.getLocation()
|
||||
if (Pathfinder.isStandingIn(x, y, s1, s1, l.getX(), l.getY(), s2, s2)) {
|
||||
if (Pathfinder.isStandingIn(x, y, s1, s1, l.x, l.y, s2, s2)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class GardenerGhostDialogue (player: Player? = null) : DialoguePlugin(player) {
|
|||
if (gardenerGhost.location.withinDistance(gardenerGhost.graveLocation, 5)) {
|
||||
sendChat(gardenerGhost, "Here is the place where I met me' maker.")
|
||||
} else {
|
||||
sendChat(gardenerGhost, "Go " + gardenerGhost.location.deriveDirection(gardenerGhost.graveLocation).name.lowercase(Locale.getDefault()).replace('_', '-') + ", mate")
|
||||
sendChat(gardenerGhost, "Go " + gardenerGhost.location.deriveDirection(gardenerGhost.graveLocation)!!.name.lowercase(Locale.getDefault()).replace('_', '-') + ", mate")
|
||||
gardenerGhost.continueFollowing(player)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ class VisualCommand : CommandPlugin() {
|
|||
return true
|
||||
}
|
||||
location = if (args.size > 2) Location.create(args[1]!!.toInt(), args[2]!!.toInt(), player!!.location.z) else player!!.location
|
||||
`object` = RegionManager.getObject(location)
|
||||
`object` = RegionManager.getObject(location!!)
|
||||
if (`object` == null) {
|
||||
player!!.debug("error: object not found in region cache.")
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1,578 +0,0 @@
|
|||
package core.game.world.map;
|
||||
|
||||
import core.game.interaction.DestinationFlag;
|
||||
import core.game.node.Node;
|
||||
import core.game.world.map.path.Path;
|
||||
import core.game.world.map.path.Pathfinder;
|
||||
import core.tools.RandomFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import core.api.utils.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.lang.Math;
|
||||
|
||||
/**
|
||||
* Represents a location on the world map.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class Location extends Node {
|
||||
|
||||
/**
|
||||
* The x-coordinate.
|
||||
*/
|
||||
private int x;
|
||||
|
||||
/**
|
||||
* The y-coordinate.
|
||||
*/
|
||||
private int y;
|
||||
|
||||
/**
|
||||
* The plane.
|
||||
*/
|
||||
private int z;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Location} {@code Object}.
|
||||
* @param x The x-coordinate.
|
||||
* @param y The y-coordinate.
|
||||
* @param z The z-coordinate.
|
||||
*/
|
||||
public Location(int x, int y, int z) {
|
||||
super(null, null);
|
||||
super.destinationFlag = DestinationFlag.LOCATION;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
if (z < 0) {
|
||||
z += 4;
|
||||
}
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Location} {@code Object}
|
||||
* @param x The x-coordinate.
|
||||
* @param y The y-coordinate.
|
||||
*/
|
||||
public Location(int x, int y) {
|
||||
this(x, y, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Location} {@code Object}.
|
||||
* @param x The x-coordinate.
|
||||
* @param y The y coordinate.
|
||||
* @param z The z-coordinate.
|
||||
* @param randomizer The amount we should randomize the x and y coordinates
|
||||
* with (x + random(randomizer), y + random(randomizer)).
|
||||
*/
|
||||
public Location(int x, int y, int z, int randomizer) {
|
||||
this(x + RandomFunction.getRandom(randomizer), y + RandomFunction.getRandom(randomizer), z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Location.
|
||||
* @param x The x-coordinate.
|
||||
* @param y The y-coordinate.
|
||||
* @param z The z-coordinate.
|
||||
* @return The constructed location.
|
||||
*/
|
||||
public static Location create(int x, int y, int z) {
|
||||
return new Location(x, y, z);
|
||||
}
|
||||
|
||||
public static Location create(int x, int y) {
|
||||
return new Location(x, y, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given location.
|
||||
* @param location The given location.
|
||||
* @return The new instance.
|
||||
*/
|
||||
public static Location create(Location location) {
|
||||
return create(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a location instance with coordinates being the difference between
|
||||
* {@code other} & {@code location}.
|
||||
* @param location The first location.
|
||||
* @param other The other location.
|
||||
* @return The delta location.
|
||||
*/
|
||||
public static Location getDelta(Location location, Location other) {
|
||||
return Location.create(other.x - location.x, other.y - location.y, other.z - location.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random location near the main location.
|
||||
* @param main The main location.
|
||||
* @param radius The radius.
|
||||
* @param reachable If the locations should be able to reach eachother.
|
||||
* @return The location.
|
||||
*/
|
||||
public static Location getRandomLocation(Location main, int radius, boolean reachable) {
|
||||
Location location = RegionManager.getTeleportLocation(main, radius);
|
||||
if (!reachable) {
|
||||
return location;
|
||||
}
|
||||
Path path = Pathfinder.find(main, location, false, Pathfinder.DUMB);
|
||||
if (!path.isSuccessful()) {
|
||||
location = main;
|
||||
if (!path.getPoints().isEmpty()) {
|
||||
Point p = path.getPoints().getLast();
|
||||
location = Location.create(p.getX(), p.getY(), main.getZ());
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this location is right next to the node (assuming the node is
|
||||
* size 1x1).
|
||||
* @param node The node to check.
|
||||
* @return {@code True} if this location is 1 tile north, west, south or
|
||||
* east of the node location.
|
||||
*/
|
||||
public boolean isNextTo(Node node) {
|
||||
Location l = node.getLocation();
|
||||
if (l.getY() == y) {
|
||||
return l.getX() - x == -1 || l.getX() - x == 1;
|
||||
}
|
||||
if (l.getX() == x) {
|
||||
return l.getY() - y == -1 || l.getY() - y == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the region id.
|
||||
* @return The region id.
|
||||
*/
|
||||
public int getRegionId() {
|
||||
return (x >> 6) << 8 | (y >> 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the users region with the one given
|
||||
* @return True if user is in given region
|
||||
*/
|
||||
public boolean isInRegion(int region) {
|
||||
return getRegionId() == region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location incremented by the given coordinates.
|
||||
* @param dir The direction to transform this location.
|
||||
* @return The location.
|
||||
*/
|
||||
public Location transform(Direction dir) {
|
||||
return transform(dir, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location incremented by the given coordinates.
|
||||
* @param dir The direction to transform this location.
|
||||
* @param steps The amount of steps to move in this direction.
|
||||
* @return The location.
|
||||
*/
|
||||
public Location transform(Direction dir, int steps) {
|
||||
return new Location(x + (dir.getStepX() * steps), y + (dir.getStepY() * steps), this.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location incremented by the given coordinates.
|
||||
* @param l incremental location
|
||||
* @return The location.
|
||||
*/
|
||||
public Location transform(Location l) {
|
||||
return new Location(x + l.getX(), y + l.getY(), this.z + l.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location incremented by the given coordinates.
|
||||
* @param diffX The x-difference.
|
||||
* @param diffY The y-difference.
|
||||
* @param z The height difference.
|
||||
* @return The location.
|
||||
*/
|
||||
public Location transform(int diffX, int diffY, int z) {
|
||||
return new Location(x + diffX, y + diffY, this.z + z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the other location is within viewing distance.
|
||||
* @param other The other location.
|
||||
* @return If you're within the other distance.
|
||||
*/
|
||||
public boolean withinDistance(Location other) {
|
||||
return withinDistance(other, MapDistance.RENDERING.getDistance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a player is within a specified distance.
|
||||
* @param other The other location.
|
||||
* @param dist The amount of distance.
|
||||
* @return If you're within the other distance.
|
||||
*/
|
||||
public boolean withinDistance(Location other, int dist) {
|
||||
if (other.z != z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int a = (other.x - x);
|
||||
int b = (other.y - y);
|
||||
double product = Math.sqrt((a*a) + (b*b));
|
||||
return product <= dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a player is within a specified distance using max norm distance.
|
||||
* @param other The other location.
|
||||
* @param dist The amount of distance.
|
||||
* @return If you're within the other distance.
|
||||
*/
|
||||
public boolean withinMaxnormDistance(Location other, int dist) {
|
||||
if (other.z != z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int a = Math.abs(other.x - x);
|
||||
int b = Math.abs(other.y - y);
|
||||
double max = Math.max(a, b);
|
||||
return max <= dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between you and the other.
|
||||
* @param other The other location.
|
||||
* @return The amount of distance between you and other.
|
||||
*/
|
||||
public double getDistance(Location other) {
|
||||
int xdiff = this.getX() - other.getX();
|
||||
int ydiff = this.getY() - other.getY();
|
||||
return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the first and the second specified distance.
|
||||
* @param first The first location.
|
||||
* @param second The other location.
|
||||
* @return The amount of distance between first and other.
|
||||
*/
|
||||
public static double getDistance(Location first, Location second) {
|
||||
int xdiff = first.getX() - second.getX();
|
||||
int ydiff = first.getY() - second.getY();
|
||||
return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 8 tiles surrounding this location as an ArrayList<Location>
|
||||
*/
|
||||
public ArrayList<Location> getSurroundingTiles() {
|
||||
ArrayList<Location> locs = new ArrayList<>();
|
||||
|
||||
locs.add(transform(-1,-1,0));//SW
|
||||
locs.add(transform(0,-1,0)); //S
|
||||
locs.add(transform(1,-1,0)); //SE
|
||||
locs.add(transform(1,0,0)); //E
|
||||
locs.add(transform(1,1,0)); //NE
|
||||
locs.add(transform(0,1,0)); //N
|
||||
locs.add(transform(-1,1,0));//NW
|
||||
locs.add(transform(-1,0,0));//W
|
||||
|
||||
return locs;
|
||||
}
|
||||
|
||||
public ArrayList<Location> getCardinalTiles() {
|
||||
ArrayList<Location> locs = new ArrayList<>();
|
||||
|
||||
locs.add(transform(-1, 0, 0));
|
||||
locs.add(transform(0, -1, 0));
|
||||
locs.add(transform(1, 0, 0));
|
||||
locs.add(transform(0, 1, 0));
|
||||
return locs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a square of 3 x 3 tiles as an ArrayList<Location>
|
||||
*/
|
||||
public ArrayList<Location> get3x3Tiles() {
|
||||
ArrayList<Location> locs = new ArrayList<>();
|
||||
locs.add(transform(0,0,0)); //Center
|
||||
locs.add(transform(0,1,0)); //N
|
||||
locs.add(transform(1,1,0)); //NE
|
||||
locs.add(transform(1,0,0)); //E
|
||||
locs.add(transform(1,-1,0)); //SE
|
||||
locs.add(transform(0,-1,0)); //S
|
||||
locs.add(transform(-1,-1,0));//SW
|
||||
locs.add(transform(-1,0,0));//W
|
||||
locs.add(transform(-1,1,0));//NW
|
||||
return locs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x position on the region chunk.
|
||||
* @return The x position on the region chunk.
|
||||
*/
|
||||
public int getChunkOffsetX() {
|
||||
int x = getLocalX();
|
||||
//return x - ((x / RegionChunk.SIZE) * RegionChunk.SIZE);
|
||||
return x & 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y position on the region chunk.
|
||||
* @return The y position on the region chunk.
|
||||
*/
|
||||
public int getChunkOffsetY() {
|
||||
int y = getLocalY();
|
||||
//return y - ((y / RegionChunk.SIZE) * RegionChunk.SIZE);
|
||||
return y & 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base location for the chunk this location is in.
|
||||
* @return The base location.
|
||||
*/
|
||||
public Location getChunkBase() {
|
||||
return create(getRegionX() << 3, getRegionY() << 3, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the region x-coordinate.
|
||||
* @return The region x-coordinate.
|
||||
*/
|
||||
public int getRegionX() {
|
||||
return x >> 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the region y-coordinate.
|
||||
* @return The region y-coordinate.
|
||||
*/
|
||||
public int getRegionY() {
|
||||
return y >> 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local x-coordinate on the current region in [0, 64).
|
||||
* @return The local x-coordinate.
|
||||
*/
|
||||
public int getLocalX() {
|
||||
return x & 63;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local y-coordinate on the current region in [0, 64).
|
||||
* @return The local y-coordinate.
|
||||
*/
|
||||
public int getLocalY() {
|
||||
return y & 63;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scene x-coordinate in [48, 55] (note that 104/2 = 52).
|
||||
* @return The local x-coordinate.
|
||||
*/
|
||||
public int getSceneX() {
|
||||
return x - ((getRegionX() - 6) << 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local y-coordinate in [48, 55] (note that 104/2 = 52).
|
||||
* @return The local y-coordinate.
|
||||
*/
|
||||
public int getSceneY() {
|
||||
return y - ((getRegionY() - 6) << 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local x-coordinate.
|
||||
* @param loc The location containing the regional coordinates.
|
||||
* @return The local x-coordinate.
|
||||
*/
|
||||
public int getSceneX(Location loc) {
|
||||
return x - ((loc.getRegionX() - 6) << 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local y-coordinate.
|
||||
* @param loc The location containing the regional coordinates.
|
||||
* @return The local y-coordinate.
|
||||
*/
|
||||
public int getSceneY(Location loc) {
|
||||
return y - ((loc.getRegionY() - 6) << 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunk's x-coordinate (0-7).
|
||||
* @return The x in the (8x8) region.
|
||||
*/
|
||||
public int getChunkX() {
|
||||
return getLocalX() >> 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chunk's y-coordinate (0-7).
|
||||
* @return The y in the (8x8) region.
|
||||
*/
|
||||
public int getChunkY() {
|
||||
return getLocalY() >> 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Location)) {
|
||||
return false;
|
||||
}
|
||||
Location loc = (Location) other;
|
||||
return loc.x == x && loc.y == y && loc.z == z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if these coordinates equal this location.
|
||||
* @param x the x.
|
||||
* @param y the y.
|
||||
* @param z the x.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean equals(int x, int y, int z) {
|
||||
return equals(new Location(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + x + ", " + y + ", " + z + "]";
|
||||
}
|
||||
|
||||
public static Location fromString(String locString) {
|
||||
String trimmed = locString.replace("[", "").replace("]", "");
|
||||
String[] tokens = trimmed.split(",");
|
||||
return Location.create(
|
||||
Integer.parseInt(tokens[0].trim()),
|
||||
Integer.parseInt(tokens[1].trim()),
|
||||
Integer.parseInt(tokens[2].trim())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return z << 30 | x << 15 | y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x.
|
||||
* @return The x.
|
||||
*/
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x.
|
||||
* @param x The x to set.
|
||||
*/
|
||||
public void setX(int x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y.
|
||||
* @return The y.
|
||||
*/
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the y.
|
||||
* @param y The y to set.
|
||||
*/
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the z.
|
||||
* @return The z.
|
||||
*/
|
||||
public int getZ() {
|
||||
return z % 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the z.
|
||||
* @param z The z to set.
|
||||
*/
|
||||
public void setZ(int z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Location> getStepComponents(Direction dir) {
|
||||
List<Location> output = new ArrayList<>(2);
|
||||
int stepX = dir.getStepX();
|
||||
int stepY = dir.getStepY();
|
||||
|
||||
// alright ill break it down real simple
|
||||
//
|
||||
// we can move diagonally but theres no melee attacking diagonally. its gotta be from N, W, S or E.
|
||||
// if we are moving diagonally, we then need to check if we can attack from one of the valid directions.
|
||||
// in other words we need to check the two tiles that share a corner with our destination and make sure they can be attacked from
|
||||
//
|
||||
// picture this: you are at (0,0). im at (1,1). you want to strangle my head off so you gotta check
|
||||
// 1. to the west of destination: (1,1) + (-1,0) = (0,1) or north from where you started
|
||||
// 2. to the south of the destination: (1,1) + (0,-1) = (1,0) or east from where you started
|
||||
// this function is used only by CombatSwingHandler.kt and assumes that we return the X tiles, west or east, FIRST
|
||||
// and then the Y tiles South and North.
|
||||
//
|
||||
// idk what else to say, if youre reading this youre either crazy or crazy good at weird ass shit like this
|
||||
// and dont even need letters to understand, theres just numbers and arrows and fking formulas or something in ur head (and a lot of work to do here)
|
||||
|
||||
if (stepX != 0) output.add(transform(-stepX, 0, 0)); // Ive never dealt with coordinate systems at this level, but it feels like Im in a cold, damp, deep and dark cave with fking goblins etc
|
||||
if (stepY != 0) output.add(transform(0, -stepY, 0)); // and minotaurs
|
||||
return output;
|
||||
}
|
||||
|
||||
public Direction deriveDirection(Location location) {
|
||||
int diffX = location.x - this.x;
|
||||
int diffY = location.y - this.y;
|
||||
|
||||
diffX = diffX >= 0 ? Math.min(diffX, 1) : -1;
|
||||
diffY = diffY >= 0 ? Math.min(diffY, 1) : -1;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (diffY != 0) {
|
||||
if (diffY > 0) {
|
||||
sb.append("NORTH");
|
||||
} else {
|
||||
sb.append("SOUTH");
|
||||
}
|
||||
}
|
||||
|
||||
if (diffX != 0) {
|
||||
if (sb.length() > 0) sb.append("_");
|
||||
if (diffX > 0) {
|
||||
sb.append("EAST");
|
||||
} else {
|
||||
sb.append("WEST");
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.length() == 0) return null;
|
||||
return Direction.valueOf(sb.toString());
|
||||
}
|
||||
|
||||
public Location transform (Vector vector) {
|
||||
return Location.create(this.x + (int) Math.floor(vector.getX()), this.y + (int) Math.floor(vector.getY()));
|
||||
}
|
||||
}
|
||||
257
Server/src/main/core/game/world/map/Location.kt
Normal file
257
Server/src/main/core/game/world/map/Location.kt
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
package core.game.world.map
|
||||
|
||||
import core.api.utils.Vector
|
||||
import core.game.interaction.DestinationFlag
|
||||
import core.game.node.Node
|
||||
import core.game.world.map.RegionManager.getTeleportLocation
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.tools.RandomFunction
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
* Specify a position (x,y,z), implements [Node].
|
||||
* - Coordinates x and y can be negative for delta values. Values ±16,383.
|
||||
* - Coordinate z (level) has a quirk, this class doesn't let you init with a negative z. Values 0 - 3.
|
||||
*/
|
||||
/*
|
||||
Official Terms
|
||||
- Zones: 8x8 tiles
|
||||
- Map Squares: 64x64 tiles
|
||||
- Build Area: 104x104 tiles (13 x 13 chunks centered around player's chunk) - client's loaded area
|
||||
|
||||
Some Definitions:
|
||||
- Cardinal: Four Base Direction (N E S W)
|
||||
- Diagonal: Four Diagonal directions (NE SE SW NW)
|
||||
- Compass: All 8 directions (For this game, compass in relation to 8-point Cardinal+Diagonal)
|
||||
- Euclidean distance: Straight-line length of two points. Direct calculation. e.g. [2,3] distance=√13(≈3.61)
|
||||
- Manhattan distance: Cardinal length of two points. Count of steps along grid lines. e.g. [2,3] distance=5
|
||||
- Chebyshev distance: Cardinal+Diagonal length of two points. Count of king moves to make. e.g. [2,3] distance=3
|
||||
*/
|
||||
class Location(var hash: LocationHash) : Node(null, null) {
|
||||
@JvmOverloads
|
||||
constructor(x: Int, y: Int, z: Int = 0) : this(
|
||||
LocationHash(x, y, (z % 4 + 4) % 4) // The previous z calc didn't make sense since they play around with negative numbers.
|
||||
)
|
||||
constructor(location: Location) : this(location.hash)
|
||||
constructor(x: Int, y: Int, z: Int, randomizer: Int) :
|
||||
this(x + RandomFunction.getRandom(randomizer), y + RandomFunction.getRandom(randomizer), z)
|
||||
init { super.destinationFlag = DestinationFlag.LOCATION } // This is to set the type of [Node].
|
||||
|
||||
// Map properties directly to the underlying hash for convenience
|
||||
var x: Int
|
||||
get() = hash.x
|
||||
set(value) { hash = LocationHash(value, y, z) }
|
||||
|
||||
var y: Int
|
||||
get() = hash.y
|
||||
set(value) { hash = LocationHash(x, value, z) }
|
||||
|
||||
var z: Int
|
||||
get() = hash.z
|
||||
set(value) { hash = LocationHash(x, y, (value % 4 + 4) % 4) }
|
||||
|
||||
companion object {
|
||||
// Alternate static constructors
|
||||
@JvmStatic @JvmOverloads fun create(x: Int, y: Int, z: Int = 0): Location = Location(x, y, z)
|
||||
@JvmStatic fun create(location: Location): Location = Location(location)
|
||||
/** @return new Location parsed from a string format "[x, y, z]". */
|
||||
fun fromString(locString: String): Location {
|
||||
val coordinates = locString.removeSurrounding("[", "]").split(",").map { it.trim().toInt() }
|
||||
return create(coordinates[0], coordinates[1], coordinates[2])
|
||||
}
|
||||
|
||||
/** @return Straight-line(Euclidean) distance between two locations. */
|
||||
@JvmStatic fun getDistance(first: Location, second: Location): Double {
|
||||
val xDiff: Int = first.x - second.x
|
||||
val yDiff: Int = first.y - second.y
|
||||
return sqrt((xDiff * xDiff + yDiff * yDiff).toDouble())
|
||||
}
|
||||
|
||||
/** @return A location representing the delta between two locations. This is not a good function because it doesn't care about z. */
|
||||
@JvmStatic fun getDelta(location: Location, other: Location): Location = create(other.x - location.x, other.y - location.y, other.z - location.z)
|
||||
|
||||
/** @return A random location within radius, optionally verifying reachability. */
|
||||
fun getRandomLocation(main: Location, radius: Int, reachable: Boolean): Location {
|
||||
val loc = getTeleportLocation(main, radius)
|
||||
if (!reachable) return loc
|
||||
val path = Pathfinder.find(main, loc, false, Pathfinder.DUMB)
|
||||
if (!path.isSuccessful && path.points.isNotEmpty()) {
|
||||
return create(path.points.last.x, path.points.last.y, main.z)
|
||||
} else if (path.isSuccessful) {
|
||||
return loc
|
||||
} else {
|
||||
return main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function overrides of Node class and Java class
|
||||
override fun getLocation(): Location = this
|
||||
override fun toString(): String = "[$x, $y, $z]"
|
||||
override fun hashCode(): Int = hash.hash
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is Location -> other.hash == this.hash
|
||||
is LocationHash -> other == this.hash
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
fun equals(x: Int, y: Int, z: Int): Boolean = hash == LocationHash(x, y, z)
|
||||
|
||||
// Derived variables. Please note the following isn't a 1-1 representation of what is officially used.
|
||||
|
||||
// Region - (Officially: Map Square) 64x64
|
||||
/** Unique index "key" (Officially: region_uid) for a 64x64 area. Made of x and y joined bitwise together. Used in xteas.json. */
|
||||
val regionId: Int get() = x shr 6 shl 8 or (y shr 6) // Cuts off the end 6 bits of both x y and joins them together. E.g. 10392
|
||||
/** The chunk x index for this position (x/8). For sets of 8x8 area. THIS IS NAMED WRONG, THIS IS A CHUNK(8) NOT REGION(64). */
|
||||
val regionX: Int get() = x shr 3 // shr 3 is essentially divide by 8
|
||||
/** The chunk y index for this position (y/8). For sets of 8x8 area. THIS IS NAMED WRONG, THIS IS A CHUNK(8) NOT REGION(64). */
|
||||
val regionY: Int get() = y shr 3 // shr 3 is essentially divide by 8
|
||||
// Local - x,y position inside a region
|
||||
/** The relative x position inside a region(64x64). */
|
||||
val localX: Int get() = x and 63 // and 63 is essentially modulo 64
|
||||
/** The relative y position inside a region(64x64). */
|
||||
val localY: Int get() = y and 63 // and 63 is essentially modulo 64
|
||||
/** @return Check if it is in the same region id. */
|
||||
fun isInRegion(region: Int): Boolean = regionId == region
|
||||
|
||||
// Chunk - (Officially: Zones) 8x8
|
||||
val chunkBase: Location get() = create(regionX shl 3, regionY shl 3, z)
|
||||
/** The chunk x index for this position RELATIVE to a region[localX](x/8). THIS IS NOT GLOBAL CHUNK X. */
|
||||
val chunkX: Int get() = localX shr 3 // shr 3 is essentially divide by 8 (note this is localX)
|
||||
/** The chunk y index for this position RELATIVE to a region[localY](y/8). THIS IS NOT GLOBAL CHUNK Y. */
|
||||
val chunkY: Int get() = localY shr 3 // shr 3 is essentially divide by 8 (note this is localX)
|
||||
/** The relative x position inside a chunk(8x8). */
|
||||
val chunkOffsetX: Int get() = localX and 7 // and 7 is essentially modulo 8
|
||||
/** The relative y position inside a chunk(8x8). */
|
||||
val chunkOffsetY: Int get() = localY and 7 // and 7 is essentially modulo 8
|
||||
|
||||
// Scene - (Officially: Build Area) 104x104 - Made of 13x13 chunks(8x8)
|
||||
/** The relative x position inside a scene(104x104). Used for client viewport rendering. */
|
||||
val sceneX: Int get() = x - (regionX - 6 shl 3) // Centers the current region at [7chunk,7chunk]
|
||||
/** The relative y position inside a scene(104x104). Used for client viewport rendering. */
|
||||
val sceneY: Int get() = y - (regionY - 6 shl 3) // Centers the current region at [7chunk,7chunk]
|
||||
/** @return The local scene x-coordinate relative to another location's regionX. */
|
||||
fun getSceneX(loc: Location): Int = x - (loc.regionX - 6 shl 3)
|
||||
/** @return The local scene y-coordinate relative to another location's regionY. */
|
||||
fun getSceneY(loc: Location): Int = y - (loc.regionY - 6 shl 3)
|
||||
|
||||
|
||||
|
||||
/** @return The location incremented by another location's coordinates. (POTENTIAL Z PROBLEMS HERE) */
|
||||
// TODO: THE FOLLOWING TRANSFORM HAS SHIT Z HANDLING.
|
||||
fun transform(l: Location): Location = Location(x + l.x, y + l.y, (z + l.z) % 4)
|
||||
/** @return The location incremented by specific x, y, z differences. */
|
||||
fun transform(diffX: Int, diffY: Int, z: Int): Location = Location(x + diffX, y + diffY, (this.z + z) % 4)
|
||||
/** @return The location incremented by direction and steps. */
|
||||
@JvmOverloads fun transform(dir: Direction, steps: Int = 1): Location = Location(x + dir.stepX * steps, y + dir.stepY * steps, z)
|
||||
/** Location transformed by a Vector. Limit usage to more complicated functionality. */
|
||||
fun transform(vector: Vector): Location = create(x + floor(vector.x).toInt(), y + floor(vector.y).toInt())
|
||||
|
||||
|
||||
/** @return True if this location is 1 tile N, W, S, or E of the node. THIS CAN BE REFACTORED OUT. */
|
||||
fun isNextTo(node: Node): Boolean {
|
||||
val l = node.location
|
||||
return if (l.y == y) abs(l.x - x) == 1 else if (l.x == x) abs(l.y - y) == 1 else false
|
||||
}
|
||||
|
||||
/** @return True if this other location is within dist of this location. */
|
||||
@JvmOverloads fun withinDistance(other: Location, dist: Int = MapDistance.RENDERING.distance): Boolean {
|
||||
if (other.z != z) return false
|
||||
return getDistance(this, other) <= dist
|
||||
}
|
||||
|
||||
/** @return True if within specified distance using max norm (Chebyshev) distance. */
|
||||
fun withinMaxnormDistance(other: Location, dist: Int): Boolean = if (other.z != z) false else max(abs(other.x - x), abs(other.y - y)) <= dist
|
||||
|
||||
/** @return The straight-line(Euclidean) distance between this and another location. */
|
||||
fun getDistance(other: Location): Double = getDistance(this, other)
|
||||
|
||||
/** @returns ArrayList of the 8 cardinal and diagonal tiles. Order is important and follows [Direction] indexing **/
|
||||
val surroundingTiles: ArrayList<Location> get() {
|
||||
val locations = ArrayList<Location>()
|
||||
locations.add(transform(-1, -1, 0))
|
||||
locations.add(transform(0, -1, 0))
|
||||
locations.add(transform(1, -1, 0))
|
||||
locations.add(transform(1, 0, 0))
|
||||
locations.add(transform(1, 1, 0))
|
||||
locations.add(transform(0, 1, 0))
|
||||
locations.add(transform(-1, 1, 0))
|
||||
locations.add(transform(-1, 0, 0))
|
||||
return locations
|
||||
}
|
||||
|
||||
/** @returns ArrayList of the 4 cardinal direction tiles. Order is important and follows [Direction] indexing **/
|
||||
val cardinalTiles: ArrayList<Location> get() {
|
||||
val locations = ArrayList<Location>()
|
||||
locations.add(transform(-1, 0, 0))
|
||||
locations.add(transform(0, -1, 0))
|
||||
locations.add(transform(1, 0, 0))
|
||||
locations.add(transform(0, 1, 0))
|
||||
return locations
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the directional components of a movement step.
|
||||
* TODO: would prefer this in the other place. This doesn't exactly belong here since this isn't a common function for other instances of Location
|
||||
*
|
||||
* we can move diagonally but there's no melee attacking diagonally. its gotta be from N, W, S or E.
|
||||
* if we are moving diagonally, we then need to check if we can attack from one of the valid directions.
|
||||
* in other words we need to check the two tiles that share a corner with our destination and make sure they can be attacked from
|
||||
*
|
||||
* picture this: you are at (0,0). im at (1,1). you want to strangle my head off, so you got to check
|
||||
* 1. to the west of destination: (1,1) + (-1,0) = (0,1) or north from where you started
|
||||
* 2. to the south of the destination: (1,1) + (0,-1) = (1,0) or east from where you started
|
||||
* this function is used only by CombatSwingHandler.kt and assumes that we return the X tiles, west or east, FIRST
|
||||
* and then the Y tiles South and North.
|
||||
*/
|
||||
fun getStepComponents(dir: Direction): List<Location> {
|
||||
val output = ArrayList<Location>(2)
|
||||
if (dir.stepX != 0) output.add(transform(-dir.stepX, 0, 0))
|
||||
if (dir.stepY != 0) output.add(transform(0, -dir.stepY, 0))
|
||||
return output
|
||||
}
|
||||
|
||||
/** @return the Direction of the another location relative to this one. */
|
||||
fun deriveDirection(location: Location): Direction? {
|
||||
val dx = (location.x - x).let { if (it >= 0) min(it, 1) else -1 }
|
||||
val dy = (location.y - y).let { if (it >= 0) min(it, 1) else -1 }
|
||||
val sb = StringBuilder()
|
||||
if (dy != 0) sb.append(if (dy > 0) "NORTH" else "SOUTH")
|
||||
if (dx != 0) { if (sb.isNotEmpty()) sb.append("_"); sb.append(if (dx > 0) "EAST" else "WEST") }
|
||||
return if (sb.isEmpty()) null else Direction.valueOf(sb.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Locations as an Integer rather than a Location object. This is for space efficiency.
|
||||
* Inner class, because this is only used here, and to avoid adding another standard until this replaces Location
|
||||
*
|
||||
* 32 bits for an int (z z x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y)
|
||||
* - Rightmost set of 15 bits is for y (can handle negative numbers)
|
||||
* - Middle set of 15 bits is for x (can handle negative numbers)
|
||||
* - Leftmost set of 2 bits is for z
|
||||
*
|
||||
* Since x and y occupy 15 bits (0 to 32,767) it becomes => 1 sign + 14 bits (e.g., 16,384) for range of ±16,383.
|
||||
*/
|
||||
@JvmInline
|
||||
value class LocationHash(val hash: Int) {
|
||||
constructor(x: Int, y: Int, z: Int = 0) : this(
|
||||
((z and 0x3) shl 30) or
|
||||
((x and 0x7FFF) shl 15) or
|
||||
(y and 0x7FFF)
|
||||
)
|
||||
val x: Int get() {
|
||||
val raw = (hash ushr 15) and 0x7FFF
|
||||
// If the 15th bit is set, it's a negative number in 15-bit range
|
||||
return if (raw >= 0x4000) raw - 0x8000 else raw
|
||||
}
|
||||
|
||||
val y: Int get() {
|
||||
val raw = hash and 0x7FFF
|
||||
// If the 15th bit is set, it's a negative number
|
||||
return if (raw >= 0x4000) raw - 0x8000 else raw
|
||||
}
|
||||
val z: Int get() = hash ushr 30
|
||||
}
|
||||
}
|
||||
|
|
@ -783,10 +783,10 @@ object RegionManager {
|
|||
for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) {
|
||||
for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) {
|
||||
for (player in forId((regionX shl 8) or regionY).planes[l.z].players) {
|
||||
if (player.location.x >= l.getX() - xdist &&
|
||||
player.location.x <= l.getX() + xdist &&
|
||||
player.location.y >= l.getY() - ydist &&
|
||||
player.location.y <= l.getY() + ydist) {
|
||||
if (player.location.x >= l.x - xdist &&
|
||||
player.location.x <= l.x + xdist &&
|
||||
player.location.y >= l.y - ydist &&
|
||||
player.location.y <= l.y + ydist) {
|
||||
players.add(player)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ class PathfinderTests {
|
|||
TestUtils.advanceTicks(5, false)
|
||||
Assertions.assertEquals(true, npc.getAttribute("return-to-spawn", false))
|
||||
TestUtils.advanceTicks(50, false)
|
||||
Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION) <= 9)
|
||||
Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION!!) <= 9)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ class PathfinderTests {
|
|||
RegionManager.forId(npc.location.regionId).flagInactive(true)
|
||||
TestUtils.advanceTicks(50, false)
|
||||
Assertions.assertEquals(false, npc.getAttribute("return-to-spawn", false))
|
||||
Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION) <= 5)
|
||||
Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION!!) <= 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue