Abstraction Layer GeoEngine.

This commit is contained in:
MobiusDevelopment
2022-07-16 06:04:42 +00:00
parent 534f5dbf33
commit beb1aa0b77
1220 changed files with 92917 additions and 72972 deletions

View File

@@ -290,7 +290,6 @@ CorrectPrices = True
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Allow characters to receive damage from falling. # Allow characters to receive damage from falling.
# CoordSynchronize = 2 is recommended.
# Default: True # Default: True
EnableFallingDamage = True EnableFallingDamage = True

View File

@@ -2,48 +2,47 @@
# Geodata # Geodata
# ================================================================= # =================================================================
# Specifies the path to geodata files. For example, when using geodata files located # Pathfinding options:
# at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ # 0 = Disabled
# 1 = Enabled using path node files.
# 2 = Enabled using geodata cells at runtime.
# Default: 0
PathFinding = 2
# Geodata file directory.
GeoDataPath = ./data/geodata/ GeoDataPath = ./data/geodata/
# Specifies the geodata files type. Default: L2J # Pathnode file directory.
# L2J: Using L2J geodata files (filename e.g. 22_16.l2j) # Default: pathnode
# L2OFF: Using L2OFF geodata files (filename e.g. 22_16_conv.dat) PathnodePath = ./data/pathnode/
GeoDataType = L2J
# ================================================================= # Pathfinding array buffers configuration.
# Pathfinding PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
# =================================================================
# When line of movement check fails, the pathfinding algoritm is performed to look for # Weight for nodes without obstacles far from walls.
# an alternative path (e.g. walk around obstacle), default: True LowWeight = 0.5
PathFinding = True
# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 # Weight for nodes near walls.
PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 MediumWeight = 2
# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 # Weight for nodes with obstacles.
MoveWeight = 10 HighWeight = 3
MoveWeightDiag = 14
# When movement flags of target node is blocked to any direction, use this weight instead of MoveWeight or MoveWeightDiag. # Angle paths will be more "smart", but in cost of higher CPU utilization.
# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 30 AdvancedDiagonalStrategy = True
ObstacleWeight = 30
# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 12 and 18 # Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True
# For proper function must be higher than MoveWeight. # Default: LowWeight * sqrt(2)
HeuristicWeight = 12 DiagonalWeight = 0.707
HeuristicWeightDiag = 18
# Maximum number of generated nodes per one path-finding process, default 3500 # Maximum number of LOS postfilter passes, 0 will disable postfilter.
MaxIterations = 3500 # Default: 3
MaxPostfilterPasses = 3
# ================================================================= # Path debug function.
# Line of Sight # Nodes known to pathfinder will be displayed as adena, constructed path as antidots.
# ================================================================= # Number of the items show node cost * 10
# Potions display path after first stage filter
# Line of sight start at X percent of the character height, default: 75 # Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter
PartOfCharacterHeight = 75 # This function FOR DEBUG PURPOSES ONLY, never use it on the live server!
DebugPath = False
# Maximum height of an obstacle, which can exceed the line of sight, default: 32
MaxObstacleHeight = 32

View File

@@ -25,6 +25,3 @@ b - Make it work
To make geodata work: To make geodata work:
* unpack your geodata files into "/data/geodata" folder (or any other folder) * unpack your geodata files into "/data/geodata" folder (or any other folder)
* open "/config/GeoEngine.ini" with your favorite text editor and then edit following configs:
- GeoDataPath = set path to your geodata, if elsewhere than "./data/geodata/"
- GeoDataType = set the geodata format, which you are using.

View File

@@ -55,8 +55,8 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX(); final int worldX = activeChar.getX();
final int worldY = activeChar.getY(); final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ(); final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX); final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY); final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{ {
@@ -73,8 +73,8 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX(); final int worldX = activeChar.getX();
final int worldY = activeChar.getY(); final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ(); final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX); final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY); final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{ {

View File

@@ -19,9 +19,9 @@ package handlers.admincommandhandlers;
import java.util.List; import java.util.List;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler; import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.util.BuilderUtil; import org.l2jmobius.gameserver.util.BuilderUtil;
@@ -37,20 +37,21 @@ public class AdminPathNode implements IAdminCommandHandler
{ {
if (command.equals("admin_path_find")) if (command.equals("admin_path_find"))
{ {
if (!Config.PATHFINDING) if (Config.PATHFINDING < 1)
{ {
BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled.");
return true; return true;
} }
if (activeChar.getTarget() != null) if (activeChar.getTarget() != null)
{ {
final List<Location> path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); final List<AbstractNodeLoc> path = PathFinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld(), true);
if (path == null) if (path == null)
{ {
BuilderUtil.sendSysMessage(activeChar, "No Route!"); BuilderUtil.sendSysMessage(activeChar, "No Route!");
return true; return true;
} }
for (Location a : path) for (AbstractNodeLoc a : path)
{ {
BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ());
} }

View File

@@ -72,7 +72,7 @@ public class AdminServerInfo implements IAdminCommandHandler
html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS); html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS);
html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute()); html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute());
html.replace("%dayNight%", GameTimeTaskManager.getInstance().isNight() ? "Night" : "Day"); html.replace("%dayNight%", GameTimeTaskManager.getInstance().isNight() ? "Night" : "Day");
html.replace("%geodata%", Config.PATHFINDING ? "Enabled" : "Disabled"); html.replace("%geodata%", Config.PATHFINDING > 0 ? "Enabled" : "Disabled");
html.replace("%serverTime%", SDF.format(new Date(System.currentTimeMillis()))); html.replace("%serverTime%", SDF.format(new Date(System.currentTimeMillis())));
html.replace("%serverUpTime%", getServerUpTime()); html.replace("%serverUpTime%", getServerUpTime());
html.replace("%onlineAll%", getPlayersCount("ALL")); html.replace("%onlineAll%", getPlayersCount("ALL"));

View File

@@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler
return null; return null;
} }
if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition))
{ {
if (sendMessage) if (sendMessage)
{ {

View File

@@ -61,7 +61,6 @@ import org.l2jmobius.commons.util.PropertiesParser;
import org.l2jmobius.commons.util.StringUtil; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.enums.ClassId; import org.l2jmobius.gameserver.enums.ClassId;
import org.l2jmobius.gameserver.enums.GeoType;
import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType; import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType;
import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder;
@@ -944,18 +943,16 @@ public class Config
// GeoEngine // GeoEngine
// -------------------------------------------------- // --------------------------------------------------
public static Path GEODATA_PATH; public static Path GEODATA_PATH;
public static GeoType GEODATA_TYPE; public static Path PATHNODE_PATH;
public static boolean PATHFINDING; public static int PATHFINDING;
public static String PATHFIND_BUFFERS; public static String PATHFIND_BUFFERS;
public static int MOVE_WEIGHT; public static float LOW_WEIGHT;
public static int MOVE_WEIGHT_DIAG; public static float MEDIUM_WEIGHT;
public static int OBSTACLE_WEIGHT; public static float HIGH_WEIGHT;
public static int OBSTACLE_WEIGHT_DIAG; public static boolean ADVANCED_DIAGONAL_STRATEGY;
public static int HEURISTIC_WEIGHT; public static float DIAGONAL_WEIGHT;
public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_POSTFILTER_PASSES;
public static int MAX_ITERATIONS; public static boolean DEBUG_PATH;
public static int PART_OF_CHARACTER_HEIGHT;
public static int MAX_OBSTACLE_HEIGHT;
/** Attribute System */ /** Attribute System */
public static int S_WEAPON_STONE; public static int S_WEAPON_STONE;
@@ -2480,19 +2477,17 @@ public class Config
// Load GeoEngine config file (if exists) // Load GeoEngine config file (if exists)
final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE);
GEODATA_PATH = Paths.get(geoEngineConfig.getString("GeoDataPath", "./data/geodata")); GEODATA_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("GeoDataPath", "geodata"));
GEODATA_TYPE = Enum.valueOf(GeoType.class, geoEngineConfig.getString("GeoDataType", "L2J")); PATHNODE_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("PathnodePath", "pathnode"));
PATHFINDING = geoEngineConfig.getBoolean("PathFinding", true); PATHFINDING = geoEngineConfig.getInt("PathFinding", 0);
PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
MOVE_WEIGHT = geoEngineConfig.getInt("MoveWeight", 10); LOW_WEIGHT = geoEngineConfig.getFloat("LowWeight", 0.5f);
MOVE_WEIGHT_DIAG = geoEngineConfig.getInt("MoveWeightDiag", 14); MEDIUM_WEIGHT = geoEngineConfig.getFloat("MediumWeight", 2);
OBSTACLE_WEIGHT = geoEngineConfig.getInt("ObstacleWeight", 30); HIGH_WEIGHT = geoEngineConfig.getFloat("HighWeight", 3);
OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); ADVANCED_DIAGONAL_STRATEGY = geoEngineConfig.getBoolean("AdvancedDiagonalStrategy", true);
HEURISTIC_WEIGHT = geoEngineConfig.getInt("HeuristicWeight", 12); DIAGONAL_WEIGHT = geoEngineConfig.getFloat("DiagonalWeight", 0.707f);
HEURISTIC_WEIGHT_DIAG = geoEngineConfig.getInt("HeuristicWeightDiag", 18); MAX_POSTFILTER_PASSES = geoEngineConfig.getInt("MaxPostfilterPasses", 3);
MAX_ITERATIONS = geoEngineConfig.getInt("MaxIterations", 3500); DEBUG_PATH = geoEngineConfig.getBoolean("DebugPath", false);
PART_OF_CHARACTER_HEIGHT = geoEngineConfig.getInt("PartOfCharacterHeight", 75);
MAX_OBSTACLE_HEIGHT = geoEngineConfig.getInt("MaxObstacleHeight", 32);
// Load AllowedPlayerRaces config file (if exists) // Load AllowedPlayerRaces config file (if exists)
final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE);

View File

@@ -1,144 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.enums;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
/**
* Container of movement constants used for various geodata and movement checks.
*/
public enum MoveDirectionType
{
N(0, -1),
S(0, 1),
W(-1, 0),
E(1, 0),
NW(-1, -1),
SW(-1, 1),
NE(1, -1),
SE(1, 1);
// Step and signum.
private final int _stepX;
private final int _stepY;
private final int _signumX;
private final int _signumY;
// Cell offset.
private final int _offsetX;
private final int _offsetY;
// Direction flags.
private final byte _directionX;
private final byte _directionY;
private final String _symbolX;
private final String _symbolY;
private MoveDirectionType(int signumX, int signumY)
{
// Get step (world -16, 0, 16) and signum (geodata -1, 0, 1) coordinates.
_stepX = signumX * GeoStructure.CELL_SIZE;
_stepY = signumY * GeoStructure.CELL_SIZE;
_signumX = signumX;
_signumY = signumY;
// Get border offsets in a direction of iteration.
_offsetX = signumX >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
_offsetY = signumY >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
// Get direction NSWE flag and symbol.
_directionX = signumX < 0 ? GeoStructure.CELL_FLAG_W : signumX == 0 ? 0 : GeoStructure.CELL_FLAG_E;
_directionY = signumY < 0 ? GeoStructure.CELL_FLAG_N : signumY == 0 ? 0 : GeoStructure.CELL_FLAG_S;
_symbolX = signumX < 0 ? "W" : signumX == 0 ? "-" : "E";
_symbolY = signumY < 0 ? "N" : signumY == 0 ? "-" : "S";
}
public int getStepX()
{
return _stepX;
}
public int getStepY()
{
return _stepY;
}
public int getSignumX()
{
return _signumX;
}
public int getSignumY()
{
return _signumY;
}
public int getOffsetX()
{
return _offsetX;
}
public int getOffsetY()
{
return _offsetY;
}
public byte getDirectionX()
{
return _directionX;
}
public byte getDirectionY()
{
return _directionY;
}
public String getSymbolX()
{
return _symbolX;
}
public String getSymbolY()
{
return _symbolY;
}
/**
* @param gdx : Geodata X delta coordinate.
* @param gdy : Geodata Y delta coordinate.
* @return {@link MoveDirectionType} based on given geodata dx and dy delta coordinates.
*/
public static MoveDirectionType getDirection(int gdx, int gdy)
{
if (gdx == 0)
{
return (gdy < 0) ? MoveDirectionType.N : MoveDirectionType.S;
}
if (gdy == 0)
{
return (gdx < 0) ? MoveDirectionType.W : MoveDirectionType.E;
}
if (gdx > 0)
{
return (gdy < 0) ? MoveDirectionType.NE : MoveDirectionType.SE;
}
return (gdy < 0) ? MoveDirectionType.NW : MoveDirectionType.SW;
}
}

View File

@@ -1,85 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
public abstract class ABlock
{
/**
* Checks the block for having geodata.
* @return boolean : True, when block has geodata (Flat, Complex, Multilayer).
*/
public abstract boolean hasGeoPos();
/**
* Returns the height of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, nearest to given coordinates.
*/
public abstract short getHeightNearest(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is closes layer to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index.
*/
public abstract int getIndexNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer above given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexAbove(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer below given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexBelow(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeight(int index);
/**
* Returns the NSWE flag byte of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract byte getNswe(int index);
}

View File

@@ -1,130 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
public class BlockComplex extends ABlock
{
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockComplex()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates ComplexBlock.
* @param bb : Input byte buffer.
*/
public BlockComplex(ByteBuffer bb)
{
// Initialize buffer.
_buffer = new byte[GeoStructure.BLOCK_CELLS * 3];
// Load data.
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
{
// Get data.
short data = bb.getShort();
// Get nswe.
_buffer[i * 3] = (byte) (data & 0x000F);
// Get height.
data = (short) ((short) (data & 0xFFF0) >> 1);
_buffer[(i * 3) + 1] = (byte) (data & 0x00FF);
_buffer[(i * 3) + 2] = (byte) (data >> 8);
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get nswe.
return _buffer[index];
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height > worldZ ? index : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height < worldZ ? index : -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@@ -1,95 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockFlat extends ABlock
{
protected final short _height;
protected byte _nswe;
/**
* Creates FlatBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockFlat(ByteBuffer bb, GeoType type)
{
// Get height and nswe.
_height = bb.getShort();
_nswe = GeoStructure.CELL_FLAG_ALL;
// Read dummy data.
if (type == GeoType.L2OFF)
{
bb.getShort();
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height > worldZ ? 0 : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height < worldZ ? 0 : -1;
}
@Override
public short getHeight(int index)
{
return _height;
}
@Override
public byte getNswe(int index)
{
return _nswe;
}
}

View File

@@ -1,248 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockMultilayer extends ABlock
{
private static final int MAX_LAYERS = Byte.MAX_VALUE;
private static ByteBuffer _temp;
/**
* Initializes the temporary buffer.
*/
public static final void initialize()
{
// Initialize temporary buffer and sorting mechanism.
_temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3);
_temp.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Releases temporary buffer.
*/
public static final void release()
{
_temp = null;
}
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockMultilayer()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates MultilayerBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockMultilayer(ByteBuffer bb, GeoType type)
{
// Move buffer pointer to end of MultilayerBlock.
for (int cell = 0; cell < GeoStructure.BLOCK_CELLS; cell++)
{
// Get layer count for this cell.
final byte layers = type != GeoType.L2OFF ? bb.get() : (byte) bb.getShort();
if ((layers <= 0) || (layers > MAX_LAYERS))
{
throw new RuntimeException("Invalid layer count for MultilayerBlock");
}
// Add layers count.
_temp.put(layers);
// Loop over layers.
for (byte layer = 0; layer < layers; layer++)
{
// Get data.
final short data = bb.getShort();
// Add nswe and height.
_temp.put((byte) (data & 0x000F));
_temp.putShort((short) ((short) (data & 0xFFF0) >> 1));
}
}
// Initialize buffer.
_buffer = Arrays.copyOf(_temp.array(), _temp.position());
// Clear temp buffer.
_temp.clear();
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get nswe.
return _buffer[index];
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
// Loop though all cell layers, find closest layer to given worldZ.
int limit = Integer.MAX_VALUE;
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Get Z distance and compare with limit.
// Note: When 2 layers have same distance to worldZ (worldZ is in the middle of them):
// > Returns bottom layer.
// >= Returns upper layer.
final int distance = Math.abs(height - worldZ);
if (distance > limit)
{
break;
}
// Update limit and move to next layer.
limit = distance;
index += 3;
}
// Return layer index.
return index - 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// Loop though all layers, find first layer above worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is higher than worldZ, return layer index.
if (height > worldZ)
{
return index;
}
// Move index to next layer.
index -= 3;
}
// No layer found.
return -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to first layer data (first from top).
byte layers = _buffer[index++];
// Loop though all layers, find first layer below worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is lower than worldZ, return layer index.
if (height < worldZ)
{
return index;
}
// Move index to next layer.
index += 3;
}
// No layer found.
return -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public final class Cell
{
/** East NSWE flag */
public static final byte NSWE_EAST = 1 << 0;
/** West NSWE flag */
public static final byte NSWE_WEST = 1 << 1;
/** South NSWE flag */
public static final byte NSWE_SOUTH = 1 << 2;
/** North NSWE flag */
public static final byte NSWE_NORTH = 1 << 3;
/** North-East NSWE flags */
public static final byte NSWE_NORTH_EAST = NSWE_NORTH | NSWE_EAST;
/** North-West NSWE flags */
public static final byte NSWE_NORTH_WEST = NSWE_NORTH | NSWE_WEST;
/** South-East NSWE flags */
public static final byte NSWE_SOUTH_EAST = NSWE_SOUTH | NSWE_EAST;
/** South-West NSWE flags */
public static final byte NSWE_SOUTH_WEST = NSWE_SOUTH | NSWE_WEST;
/** All directions NSWE flags */
public static final byte NSWE_ALL = NSWE_EAST | NSWE_WEST | NSWE_SOUTH | NSWE_NORTH;
private Cell()
{
}
}

View File

@@ -0,0 +1,162 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.l2jmobius.gameserver.geoengine.geodata.regions.NullRegion;
import org.l2jmobius.gameserver.geoengine.geodata.regions.Region;
/**
* @author HorridoJoho
*/
public final class GeoData
{
// world dimensions: 1048576 * 1048576 = 1099511627776
private static final int WORLD_MIN_X = -655360;
private static final int WORLD_MAX_X = 393215;
private static final int WORLD_MIN_Y = -589824;
private static final int WORLD_MAX_Y = 458751;
/** Regions in the world on the x axis */
public static final int GEO_REGIONS_X = 32;
/** Regions in the world on the y axis */
public static final int GEO_REGIONS_Y = 32;
/** Region in the world */
public static final int GEO_REGIONS = GEO_REGIONS_X * GEO_REGIONS_Y;
/** Blocks in the world on the x axis */
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * IRegion.REGION_BLOCKS_X;
/** Blocks in the world on the y axis */
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * IRegion.REGION_BLOCKS_Y;
/** Blocks in the world */
public static final int GEO_BLOCKS = GEO_REGIONS * IRegion.REGION_BLOCKS;
/** Cells in the world on the x axis */
public static final int GEO_CELLS_X = GEO_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in the world in the y axis */
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** The regions array */
private final AtomicReferenceArray<IRegion> _regions = new AtomicReferenceArray<>(GEO_REGIONS);
public GeoData()
{
for (int i = 0; i < _regions.length(); i++)
{
_regions.set(i, NullRegion.INSTANCE);
}
}
private void checkGeoX(int geoX)
{
if ((geoX < 0) || (geoX >= GEO_CELLS_X))
{
throw new IllegalArgumentException();
}
}
private void checkGeoY(int geoY)
{
if ((geoY < 0) || (geoY >= GEO_CELLS_Y))
{
throw new IllegalArgumentException();
}
}
private IRegion getRegion(int geoX, int geoY)
{
checkGeoX(geoX);
checkGeoY(geoY);
return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y));
}
public void loadRegion(Path filePath, int regionX, int regionY) throws IOException
{
final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY;
try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r"))
{
_regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).order(ByteOrder.LITTLE_ENDIAN)));
}
}
public void unloadRegion(int regionX, int regionY)
{
_regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE);
}
public boolean hasGeoPos(int geoX, int geoY)
{
return getRegion(geoX, geoY).hasGeo();
}
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
public int getGeoX(int worldX)
{
if ((worldX < WORLD_MIN_X) || (worldX > WORLD_MAX_X))
{
throw new IllegalArgumentException();
}
return (worldX - WORLD_MIN_X) / 16;
}
public int getGeoY(int worldY)
{
if ((worldY < WORLD_MIN_Y) || (worldY > WORLD_MAX_Y))
{
throw new IllegalArgumentException();
}
return (worldY - WORLD_MIN_Y) / 16;
}
public int getWorldX(int geoX)
{
checkGeoX(geoX);
return (geoX * 16) + WORLD_MIN_X + 8;
}
public int getWorldY(int geoY)
{
checkGeoY(geoY);
return (geoY * 16) + WORLD_MIN_Y + 8;
}
}

View File

@@ -1,81 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import org.l2jmobius.gameserver.model.World;
public final class GeoStructure
{
// Geo cell direction (nswe) flags.
public static final byte CELL_FLAG_NONE = 0x00;
public static final byte CELL_FLAG_E = 0x01;
public static final byte CELL_FLAG_W = 0x02;
public static final byte CELL_FLAG_S = 0x04;
public static final byte CELL_FLAG_N = 0x08;
public static final byte CELL_FLAG_SE = 0x10;
public static final byte CELL_FLAG_SW = 0x20;
public static final byte CELL_FLAG_NE = 0x40;
public static final byte CELL_FLAG_NW = (byte) 0x80;
public static final byte CELL_FLAG_ALL = 0x0F;
// Geo cell expansion flags
public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW;
public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE;
public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E;
public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W;
public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E;
public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W;
// public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW;
public static final byte CELL_EXPANSION_ALL = (byte) 0xFF;
// Geo cell height constants.
public static final int CELL_SIZE = 16;
public static final int CELL_HEIGHT = 8;
public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6;
// Geo block type identification.
public static final byte TYPE_FLAT_L2J_L2OFF = 0;
public static final byte TYPE_COMPLEX_L2J = 1;
public static final byte TYPE_COMPLEX_L2OFF = 0x40;
public static final byte TYPE_MULTILAYER_L2J = 2;
// public static final byte TYPE_MULTILAYER_L2OFF = 0x41; // officially not does exist, is anything above complex block (0x41 - 0xFFFF)
// Geo block dimensions.
public static final int BLOCK_CELLS_X = 8;
public static final int BLOCK_CELLS_Y = 8;
public static final int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
// Geo region dimensions.
public static final int REGION_BLOCKS_X = 256;
public static final int REGION_BLOCKS_Y = 256;
public static final int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
public static final int REGION_CELLS_X = REGION_BLOCKS_X * BLOCK_CELLS_X;
public static final int REGION_CELLS_Y = REGION_BLOCKS_Y * BLOCK_CELLS_Y;
// Geo world dimensions.
public static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1);
public static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.TILE_Y_MIN) + 1);
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * REGION_BLOCKS_X;
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * REGION_BLOCKS_Y;
public static final int GEO_CELLS_X = GEO_BLOCKS_X * BLOCK_CELLS_X;
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * BLOCK_CELLS_Y;
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IBlock
{
int TYPE_FLAT = 0;
int TYPE_COMPLEX = 1;
int TYPE_MULTILAYER = 2;
/** Cells in a block on the x axis */
int BLOCK_CELLS_X = 8;
/** Cells in a block on the y axis */
int BLOCK_CELLS_Y = 8;
/** Cells in a block */
int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IRegion
{
/** Blocks in a region on the x axis. */
int REGION_BLOCKS_X = 256;
/** Blocks in a region on the y axis. */
int REGION_BLOCKS_Y = 256;
/** Blocks in a region. */
int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
/** Cells in a region on the x axis. */
int REGION_CELLS_X = REGION_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in a regioin on the y axis. */
int REGION_CELLS_Y = REGION_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** Cells in a region. */
int REGION_CELLS = REGION_CELLS_X * REGION_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
boolean hasGeo();
}

View File

@@ -0,0 +1,79 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public final class ComplexBlock implements IBlock
{
private final short[] _data;
public ComplexBlock(ByteBuffer bb)
{
_data = new short[IBlock.BLOCK_CELLS];
for (int cellOffset = 0; cellOffset < IBlock.BLOCK_CELLS; cellOffset++)
{
_data[cellOffset] = bb.getShort();
}
}
private short getCellData(int geoX, int geoY)
{
return _data[((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y)];
}
private byte getCellNSWE(int geoX, int geoY)
{
return (byte) (getCellData(geoX, geoY) & 0x000F);
}
private int getCellHeight(int geoX, int geoY)
{
return (short) (getCellData(geoX, geoY) & 0x0FFF0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (getCellNSWE(geoX, geoY) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getCellHeight(geoX, geoY);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = getCellHeight(geoX, geoY);
return cellHeight <= worldZ ? cellHeight : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = getCellHeight(geoX, geoY);
return cellHeight >= worldZ ? cellHeight : worldZ;
}
}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public class FlatBlock implements IBlock
{
private final short _height;
public FlatBlock(ByteBuffer bb)
{
_height = bb.getShort();
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return _height <= worldZ ? _height : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return _height >= worldZ ? _height : worldZ;
}
}

View File

@@ -0,0 +1,185 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public class MultilayerBlock implements IBlock
{
private final byte[] _data;
/**
* Initializes a new instance of this block reading the specified buffer.
* @param bb the buffer
*/
public MultilayerBlock(ByteBuffer bb)
{
final int start = bb.position();
for (int blockCellOffset = 0; blockCellOffset < IBlock.BLOCK_CELLS; blockCellOffset++)
{
final byte nLayers = bb.get();
if ((nLayers <= 0) || (nLayers > 125))
{
throw new RuntimeException("L2JGeoDriver: Geo file corrupted! Invalid layers count!");
}
bb.position(bb.position() + (nLayers * 2));
}
_data = new byte[bb.position() - start];
bb.position(start);
bb.get(_data);
}
private short getNearestLayer(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
// One layer at least was required on loading so this is set at least once on the loop below.
int nearestDZ = 0;
short nearestData = 0;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerData;
}
final int layerDZ = Math.abs(layerZ - worldZ);
if ((offset == (startOffset + 1)) || (layerDZ < nearestDZ))
{
nearestDZ = layerDZ;
nearestData = layerData;
}
}
return nearestData;
}
private int getCellDataOffset(int geoX, int geoY)
{
final int cellLocalOffset = ((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y);
int cellDataOffset = 0;
// Move index to cell, we need to parse on each request, OR we parse on creation and save indexes.
for (int i = 0; i < cellLocalOffset; i++)
{
cellDataOffset += 1 + (_data[cellDataOffset] * 2);
}
// Now the index points to the cell we need.
return cellDataOffset;
}
private short extractLayerData(int dataOffset)
{
return (short) ((_data[dataOffset] & 0xFF) | (_data[dataOffset + 1] << 8));
}
private int getNearestNSWE(int geoX, int geoY, int worldZ)
{
return extractLayerNswe(getNearestLayer(geoX, geoY, worldZ));
}
private int extractLayerNswe(short layer)
{
return (byte) (layer & 0x000F);
}
private int extractLayerHeight(short layer)
{
return (short) (layer & 0x0fff0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (getNearestNSWE(geoX, geoY, worldZ) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return extractLayerHeight(getNearestLayer(geoX, geoY, worldZ));
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int lowerZ = Integer.MIN_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerZ;
}
if ((layerZ < worldZ) && (layerZ > lowerZ))
{
lowerZ = layerZ;
}
}
return lowerZ == Integer.MIN_VALUE ? worldZ : lowerZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int higherZ = Integer.MAX_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerZ;
}
if ((layerZ > worldZ) && (layerZ < higherZ))
{
higherZ = layerZ;
}
}
return higherZ == Integer.MAX_VALUE ? worldZ : higherZ;
}
}

View File

@@ -14,55 +14,44 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.l2jmobius.gameserver.geoengine.geodata; package org.l2jmobius.gameserver.geoengine.geodata.regions;
public class BlockNull extends ABlock import org.l2jmobius.gameserver.geoengine.geodata.IRegion;
/**
* @author HorridoJoho
*/
public final class NullRegion implements IRegion
{ {
public static final NullRegion INSTANCE = new NullRegion();
@Override @Override
public boolean hasGeoPos() public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public boolean hasGeo()
{ {
return false; return false;
} }
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return GeoStructure.CELL_FLAG_ALL;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public short getHeight(int index)
{
return 0;
}
@Override
public byte getNswe(int index)
{
return GeoStructure.CELL_FLAG_ALL;
}
}

View File

@@ -0,0 +1,98 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.regions;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
import org.l2jmobius.gameserver.geoengine.geodata.IRegion;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.ComplexBlock;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.FlatBlock;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.MultilayerBlock;
/**
* @author HorridoJoho
*/
public final class Region implements IRegion
{
private final IBlock[] _blocks = new IBlock[IRegion.REGION_BLOCKS];
public Region(ByteBuffer bb)
{
for (int blockOffset = 0; blockOffset < IRegion.REGION_BLOCKS; blockOffset++)
{
final int blockType = bb.get();
switch (blockType)
{
case IBlock.TYPE_FLAT:
{
_blocks[blockOffset] = new FlatBlock(bb);
break;
}
case IBlock.TYPE_COMPLEX:
{
_blocks[blockOffset] = new ComplexBlock(bb);
break;
}
case IBlock.TYPE_MULTILAYER:
{
_blocks[blockOffset] = new MultilayerBlock(bb);
break;
}
default:
{
throw new RuntimeException("Invalid block type " + blockType + "!");
}
}
}
}
private IBlock getBlock(int geoX, int geoY)
{
return _blocks[(((geoX / IBlock.BLOCK_CELLS_X) % IRegion.REGION_BLOCKS_X) * IRegion.REGION_BLOCKS_Y) + ((geoY / IBlock.BLOCK_CELLS_Y) % IRegion.REGION_BLOCKS_Y)];
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getBlock(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
@Override
public boolean hasGeo()
{
return true;
}
}

View File

@@ -0,0 +1,84 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
public abstract class AbstractNode<Loc extends AbstractNodeLoc>
{
private Loc _loc;
private AbstractNode<Loc> _parent;
public AbstractNode(Loc loc)
{
_loc = loc;
}
public void setParent(AbstractNode<Loc> p)
{
_parent = p;
}
public AbstractNode<Loc> getParent()
{
return _parent;
}
public Loc getLoc()
{
return _loc;
}
public void setLoc(Loc l)
{
_loc = l;
}
@Override
public int hashCode()
{
return (31 * 1) + ((_loc == null) ? 0 : _loc.hashCode());
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof AbstractNode))
{
return false;
}
final AbstractNode<?> other = (AbstractNode<?>) obj;
if (_loc == null)
{
if (other._loc != null)
{
return false;
}
}
else if (!_loc.equals(other._loc))
{
return false;
}
return true;
}
}

View File

@@ -14,22 +14,22 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.l2jmobius.gameserver.enums; package org.l2jmobius.gameserver.geoengine.pathfinding;
public enum GeoType /**
* @author -Nemesiss-
*/
public abstract class AbstractNodeLoc
{ {
L2J("%d_%d.l2j"), public abstract int getX();
L2OFF("%d_%d_conv.dat");
private final String _filename; public abstract int getY();
private GeoType(String filename) public abstract int getZ();
{
_filename = filename;
}
public String getFilename() public abstract void setZ(short z);
{
return _filename; public abstract int getNodeX();
}
} public abstract int getNodeY();
}

View File

@@ -1,119 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.model.Location;
public class Node extends Location implements Comparable<Node>
{
// Node geodata values.
private int _geoX;
private int _geoY;
private byte _nswe;
private byte _nsweExpand;
// The cost G (movement cost done) and cost H (estimated cost to target).
private int _costG;
private int _costH;
private int _costF;
// Node parent (reverse path construction).
private Node _parent;
public Node()
{
super(0, 0, 0);
}
@Override
public void clean()
{
super.clean();
_geoX = 0;
_geoY = 0;
_nswe = GeoStructure.CELL_FLAG_NONE;
_nsweExpand = GeoStructure.CELL_FLAG_NONE;
_costG = 0;
_costH = 0;
_costF = 0;
_parent = null;
}
public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand)
{
super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz);
_geoX = gx;
_geoY = gy;
_nswe = nswe;
_nsweExpand = nsweExpand;
}
public void setCost(Node parent, int weight, int costH)
{
_costG = weight;
if (parent != null)
{
_costG += parent._costG;
}
_costH = costH;
_costF = _costG + _costH;
_parent = parent;
}
public int getGeoX()
{
return _geoX;
}
public int getGeoY()
{
return _geoY;
}
public byte getNSWE()
{
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;
}
public Node getParent()
{
return _parent;
}
@Override
public int compareTo(Node o)
{
return _costF - o._costF;
}
}

View File

@@ -1,543 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.locks.ReentrantLock;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.ABlock;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.model.Location;
public class NodeBuffer
{
// Locking NodeBuffer to ensure thread-safe operations.
private final ReentrantLock _lock = new ReentrantLock();
// Container holding all available Nodes to be used.
private final Node[] _buffer;
private int _bufferIndex;
// Container (binary-heap) holding Nodes to be explored.
private final PriorityQueue<Node> _opened;
// Container holding Nodes already explored.
private final List<Node> _closed;
// Target coordinates.
private int _gtx;
private int _gty;
private int _gtz;
private Node _current;
/**
* Constructor of NodeBuffer.
* @param size : The total size buffer. Determines the amount of {@link Node}s to be used for pathfinding.
*/
public NodeBuffer(int size)
{
// Create buffers based on given size.
_buffer = new Node[size];
_opened = new PriorityQueue<>(size);
_closed = new ArrayList<>(size);
// Create Nodes.
for (int i = 0; i < size; i++)
{
_buffer[i] = new Node();
}
}
/**
* Find path consisting of Nodes. Starts at origin coordinates, ends in target coordinates.
* @param gox : origin point x
* @param goy : origin point y
* @param goz : origin point z
* @param gtx : target point x
* @param gty : target point y
* @param gtz : target point z
* @return The list of {@link Location} for the path. Empty, if path not found.
*/
public List<Location> findPath(int gox, int goy, int goz, int gtx, int gty, int gtz)
{
// Set target coordinates.
_gtx = gtx;
_gty = gty;
_gtz = gtz;
// Get node from buffer.
_current = _buffer[_bufferIndex++];
// Set node geodata coordinates and movement cost.
_current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL);
_current.setCost(null, 0, getCostH(gox, goy, goz));
int count = 0;
do
{
// Move node to closed list.
_closed.add(_current);
// Target reached, calculate path and return.
if ((_current.getGeoX() == _gtx) && (_current.getGeoY() == _gty) && (_current.getZ() == _gtz))
{
return constructPath();
}
// Expand current node.
expand();
// Get next node to expand.
_current = _opened.poll();
}
while ((_current != null) && (_bufferIndex < _buffer.length) && (++count < Config.MAX_ITERATIONS));
// Iteration failed, return empty path.
return Collections.emptyList();
}
/**
* Build the path from subsequent nodes. Skip nodes in straight directions, keep only corner nodes.
* @return List of {@link Node}s representing the path.
*/
private List<Location> constructPath()
{
// Create result.
final LinkedList<Location> path = new LinkedList<>();
// Clear X/Y direction.
int dx = 0;
int dy = 0;
// Get parent node.
Node parent = _current.getParent();
// While parent exists.
while (parent != null)
{
// Get parent node to current node X/Y direction.
final int nx = parent.getGeoX() - _current.getGeoX();
final int ny = parent.getGeoY() - _current.getGeoY();
// Direction has changed?
if ((dx != nx) || (dy != ny))
{
// Add current node to the beginning of the path (Node must be cloned, as NodeBuffer reuses them).
path.addFirst(_current.clone());
// Update X/Y direction.
dx = nx;
dy = ny;
}
// Move current node and update its parent.
_current = parent;
parent = _current.getParent();
}
return path;
}
public boolean isLocked()
{
return _lock.tryLock();
}
public void free()
{
_opened.clear();
_closed.clear();
for (int i = 0; i < (_bufferIndex - 1); i++)
{
_buffer[i].clean();
}
_bufferIndex = 0;
_current = null;
_lock.unlock();
}
/**
* Expand the current {@link Node} by exploring its neighbors (axially and diagonally).
*/
private void expand()
{
// Movement is blocked, skip.
final byte nswe = _current.getNSWE();
byte expand = _current.getNsweExpand();
if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE)
{
return;
}
// Get geo coordinates of the node to be expanded.
// Note: Z coord shifted up to avoid dual-layer issues.
final int x = _current.getGeoX();
final int y = _current.getGeoY();
final int z = _current.getZ() + GeoStructure.CELL_IGNORE_HEIGHT;
byte nsweN = GeoStructure.CELL_FLAG_NONE;
byte nsweS = GeoStructure.CELL_FLAG_NONE;
byte nsweW = GeoStructure.CELL_FLAG_NONE;
byte nsweE = GeoStructure.CELL_FLAG_NONE;
switch (expand)
{
case GeoStructure.CELL_EXPANSION_N:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_S:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_W:
{
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))
{
if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))
{
if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_E:
{
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))
{
if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))
{
if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_NW:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_NE:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_SW:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_SE:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_ALL:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
break;
}
}
}
private static byte getNodeNswe(int gx, int gy, int gz)
{
// Check new node is out of geodata grid (world coordinates).
if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y))
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get geodata block and check if there is a layer at given coordinates.
ABlock block = GeoEngine.getInstance().getBlock(gx, gy);
final int index = block.getIndexBelow(gx, gy, gz);
if (index < 0)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get node geodata nswe.
return block.getNswe(index);
}
/**
* Take {@link Node} from buffer, validate it and add to opened list.
* @param gx : The new node X geodata coordinate.
* @param gy : The new node Y geodata coordinate.
* @param gzValue : The new node Z geodata coordinate.
* @param nsweExpand : The allowed directions to be expanded on the new node.
* @param diagonal : The new node is being explored in diagonal direction.
* @return The nswe of the added node. Blank, if not added.
*/
private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal)
{
// Check new node is out of geodata grid (world coordinates).
if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y))
{
return GeoStructure.CELL_FLAG_NONE;
}
// Check buffer has reached capacity.
if (_bufferIndex >= _buffer.length)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get geodata block and check if there is a layer at given coordinates.
final ABlock block = GeoEngine.getInstance().getBlock(gx, gy);
final int index = block.getIndexBelow(gx, gy, gzValue);
if (index < 0)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get node geodata Z and nswe.
final int gz = block.getHeight(index);
final byte nswe = block.getNswe(index);
// Get node from current index (don't move index yet).
Node node = _buffer[_bufferIndex];
// Calculate node weight.
int weight;
if (nswe == GeoStructure.CELL_FLAG_ALL)
{
weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT;
}
else
{
weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT;
}
// Set node geodata coordinates.
node.setGeo(gx, gy, gz, nswe, nsweExpand);
// Node is already added to opened list, return.
if (_opened.contains(node))
{
return nswe;
}
// Node was already expanded, return.
if (_closed.contains(node))
{
return nswe;
}
// The node is to be used. Set node movement cost and add it to opened list. Move the buffer index.
node.setCost(_current, weight, getCostH(gx, gy, gz));
_opened.add(node);
_bufferIndex++;
return nswe;
}
/**
* Calculate cost H value, calculated using diagonal distance method.<br>
* Note: Manhattan distance is too simple, causing to explore more unwanted cells.
* @param gx : The node geodata X coordinate.
* @param gy : The node geodata Y coordinate.
* @param gz : The node geodata Z coordinate.
* @return The cost H value (estimated cost to reach the target).
*/
private int getCostH(int gx, int gy, int gz)
{
// Get differences to the target.
int dx = Math.abs(gx - _gtx);
int dy = Math.abs(gy - _gty);
int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT;
// Get diagonal and axial differences to the target.
final int ds = Math.min(dx, Math.min(dy, dz));
dx -= ds;
dy -= ds;
dz -= ds;
int dd;
int d;
if (dx == 0)
{
dd = Math.min(dy, dz);
d = Math.max(dy, dz) - dd;
}
else if (dy == 0)
{
dd = Math.min(dx, dz);
d = Math.max(dx, dz) - dd;
}
else
{
dd = Math.min(dx, dy);
d = Math.max(dx, dy) - dd;
}
// Calculate the diagonal distance of the node to the target.
return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2);
}
}

View File

@@ -0,0 +1,100 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.List;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes.CellPathFinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.geonodes.GeoPathFinding;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author -Nemesiss-
*/
public abstract class PathFinding
{
public static PathFinding getInstance()
{
return Config.PATHFINDING == 1 ? GeoPathFinding.getInstance() : CellPathFinding.getInstance();
}
public abstract boolean pathNodesExist(short regionoffset);
public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable);
/**
* Convert geodata position to pathnode position
* @param geoPos
* @return pathnode position
*/
public short getNodePos(int geoPos)
{
return (short) (geoPos >> 3); // OK?
}
/**
* Convert node position to pathnode block position
* @param nodePos
* @return pathnode block position (0...255)
*/
public short getNodeBlock(int nodePos)
{
return (short) (nodePos % 256);
}
public byte getRegionX(int nodePos)
{
return (byte) ((nodePos >> 8) + World.TILE_X_MIN);
}
public byte getRegionY(int nodePos)
{
return (byte) ((nodePos >> 8) + World.TILE_Y_MIN);
}
public short getRegionOffset(byte rx, byte ry)
{
return (short) ((rx << 5) + ry);
}
/**
* Convert pathnode x to World x position
* @param nodeX rx
* @return
*/
public int calculateWorldX(short nodeX)
{
return World.WORLD_X_MIN + (nodeX * 128) + 48;
}
/**
* Convert pathnode y to World y position
* @param nodeY
* @return
*/
public int calculateWorldY(short nodeY)
{
return World.WORLD_Y_MIN + (nodeY * 128) + 48;
}
public String[] getStat()
{
return null;
}
}

View File

@@ -0,0 +1,69 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
public class CellNode extends AbstractNode<NodeLoc>
{
private CellNode _next = null;
private boolean _isInUse = true;
private float _cost = -1000;
public CellNode(NodeLoc loc)
{
super(loc);
}
public boolean isInUse()
{
return _isInUse;
}
public void setInUse()
{
_isInUse = true;
}
public CellNode getNext()
{
return _next;
}
public void setNext(CellNode next)
{
_next = next;
}
public float getCost()
{
return _cost;
}
public void setCost(double cost)
{
_cost = (float) cost;
}
public void free()
{
setParent(null);
_cost = -1000;
_isInUse = false;
_next = null;
}
}

View File

@@ -0,0 +1,335 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.l2jmobius.Config;
/**
* @author DS Credits to Diamond
*/
public class CellNodeBuffer
{
private static final int MAX_ITERATIONS = 3500;
private final ReentrantLock _lock = new ReentrantLock();
private final int _mapSize;
private final CellNode[][] _buffer;
private int _baseX = 0;
private int _baseY = 0;
private int _targetX = 0;
private int _targetY = 0;
private int _targetZ = 0;
private long _timeStamp = 0;
private long _lastElapsedTime = 0;
private CellNode _current = null;
public CellNodeBuffer(int size)
{
_mapSize = size;
_buffer = new CellNode[_mapSize][_mapSize];
}
public final boolean lock()
{
return _lock.tryLock();
}
public final CellNode findPath(int x, int y, int z, int tx, int ty, int tz)
{
_timeStamp = System.currentTimeMillis();
_baseX = x + ((tx - x - _mapSize) / 2); // Middle of the line (x,y) - (tx,ty).
_baseY = y + ((ty - y - _mapSize) / 2); // Will be in the center of the buffer.
_targetX = tx;
_targetY = ty;
_targetZ = tz;
_current = getNode(x, y, z);
_current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
for (int count = 0; count < MAX_ITERATIONS; count++)
{
if ((_current.getLoc().getNodeX() == _targetX) && (_current.getLoc().getNodeY() == _targetY) && (Math.abs(_current.getLoc().getZ() - _targetZ) < 64))
{
return _current; // Found.
}
getNeighbors();
if (_current.getNext() == null)
{
return null; // No more ways.
}
_current = _current.getNext();
}
return null;
}
public final void free()
{
_current = null;
CellNode node;
for (int i = 0; i < _mapSize; i++)
{
for (int j = 0; j < _mapSize; j++)
{
node = _buffer[i][j];
if (node != null)
{
node.free();
}
}
}
_lock.unlock();
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
}
public final long getElapsedTime()
{
return _lastElapsedTime;
}
public final List<CellNode> debugPath()
{
final List<CellNode> result = new LinkedList<>();
for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
{
result.add(n);
n.setCost(-n.getCost());
}
for (int i = 0; i < _mapSize; i++)
{
for (int j = 0; j < _mapSize; j++)
{
final CellNode n = _buffer[i][j];
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
{
continue;
}
result.add(n);
}
}
return result;
}
private final void getNeighbors()
{
if (_current.getLoc().canGoNone())
{
return;
}
final int x = _current.getLoc().getNodeX();
final int y = _current.getLoc().getNodeY();
final int z = _current.getLoc().getZ();
CellNode nodeE = null;
CellNode nodeS = null;
CellNode nodeW = null;
CellNode nodeN = null;
// East
if (_current.getLoc().canGoEast())
{
nodeE = addNode(x + 1, y, z, false);
}
// South
if (_current.getLoc().canGoSouth())
{
nodeS = addNode(x, y + 1, z, false);
}
// West
if (_current.getLoc().canGoWest())
{
nodeW = addNode(x - 1, y, z, false);
}
// North
if (_current.getLoc().canGoNorth())
{
nodeN = addNode(x, y - 1, z, false);
}
if (!Config.ADVANCED_DIAGONAL_STRATEGY)
{
return;
}
// SouthEast
if ((nodeE != null) && (nodeS != null) && nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast())
{
addNode(x + 1, y + 1, z, true);
}
// SouthWest
if ((nodeS != null) && (nodeW != null) && nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest())
{
addNode(x - 1, y + 1, z, true);
}
// NorthEast
if ((nodeN != null) && (nodeE != null) && nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast())
{
addNode(x + 1, y - 1, z, true);
}
// NorthWest
if ((nodeN != null) && (nodeW != null) && nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest())
{
addNode(x - 1, y - 1, z, true);
}
}
private final CellNode getNode(int x, int y, int z)
{
final int aX = x - _baseX;
if ((aX < 0) || (aX >= _mapSize))
{
return null;
}
final int aY = y - _baseY;
if ((aY < 0) || (aY >= _mapSize))
{
return null;
}
CellNode result = _buffer[aX][aY];
if (result == null)
{
result = new CellNode(new NodeLoc(x, y, z));
_buffer[aX][aY] = result;
}
else if (!result.isInUse())
{
result.setInUse();
// Re-init node if needed.
if (result.getLoc() != null)
{
result.getLoc().set(x, y, z);
}
else
{
result.setLoc(new NodeLoc(x, y, z));
}
}
return result;
}
private final CellNode addNode(int x, int y, int z, boolean diagonal)
{
final CellNode newNode = getNode(x, y, z);
if (newNode == null)
{
return null;
}
if (newNode.getCost() >= 0)
{
return newNode;
}
final int geoZ = newNode.getLoc().getZ();
final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
if (!newNode.getLoc().canGoAll() || (stepZ > 16))
{
weight = Config.HIGH_WEIGHT;
}
else if (isHighWeight(x + 1, y, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x - 1, y, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x, y + 1, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x, y - 1, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
newNode.setParent(_current);
newNode.setCost(getCost(x, y, geoZ, weight));
CellNode node = _current;
int count = 0;
while ((node.getNext() != null) && (count < (MAX_ITERATIONS * 4)))
{
count++;
if (node.getNext().getCost() > newNode.getCost())
{
// Insert node into a chain.
newNode.setNext(node.getNext());
break;
}
node = node.getNext();
}
if (count == (MAX_ITERATIONS * 4))
{
System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
}
node.setNext(newNode); // Add last.
return newNode;
}
private final boolean isHighWeight(int x, int y, int z)
{
final CellNode result = getNode(x, y, z);
return (result == null) || !result.getLoc().canGoAll() || (Math.abs(result.getLoc().getZ() - z) > 16);
}
private final double getCost(int x, int y, int z, float weight)
{
final int dX = x - _targetX;
final int dY = y - _targetY;
final int dZ = z - _targetZ;
// Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16
double result = Math.sqrt((dX * dX) + (dY * dY) + ((dZ * dZ) / 256.0));
if (result > weight)
{
result += weight;
}
if (result > Float.MAX_VALUE)
{
result = Float.MAX_VALUE;
}
return result;
}
}

View File

@@ -0,0 +1,403 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.instancemanager.IdManager;
import org.l2jmobius.gameserver.model.instancezone.Instance;
import org.l2jmobius.gameserver.model.item.instance.Item;
/**
* @author Sami, DS Credits to Diamond
*/
public class CellPathFinding extends PathFinding
{
private static final Logger LOGGER = Logger.getLogger(CellPathFinding.class.getName());
private BufferInfo[] _allBuffers;
private int _findSuccess = 0;
private int _findFails = 0;
private int _postFilterUses = 0;
private int _postFilterPlayableUses = 0;
private int _postFilterPasses = 0;
private long _postFilterElapsed = 0;
private List<Item> _debugItems = null;
protected CellPathFinding()
{
try
{
final String[] array = Config.PATHFIND_BUFFERS.split(";");
_allBuffers = new BufferInfo[array.length];
String buf;
String[] args;
for (int i = 0; i < array.length; i++)
{
buf = array[i];
args = buf.split("x");
if (args.length != 2)
{
throw new Exception("Invalid buffer definition: " + buf);
}
_allBuffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e);
throw new Error("CellPathFinding: load aborted");
}
}
@Override
public boolean pathNodesExist(short regionoffset)
{
return false;
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
{
final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getInstance().getGeoY(y);
if (!GeoEngine.getInstance().hasGeo(x, y))
{
return null;
}
final int gz = GeoEngine.getInstance().getHeight(x, y, z);
final int gtx = GeoEngine.getInstance().getGeoX(tx);
final int gty = GeoEngine.getInstance().getGeoY(ty);
if (!GeoEngine.getInstance().hasGeo(tx, ty))
{
return null;
}
final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz);
final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty))), playable);
if (buffer == null)
{
return null;
}
final boolean debug = Config.DEBUG_PATH && playable;
if (debug)
{
if (_debugItems == null)
{
_debugItems = new CopyOnWriteArrayList<>();
}
else
{
for (Item item : _debugItems)
{
item.decayMe();
}
_debugItems.clear();
}
}
List<AbstractNodeLoc> path = null;
try
{
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
if (debug)
{
for (CellNode n : buffer.debugPath())
{
if (n.getCost() < 0)
{
dropDebugItem(1831, (int) (-n.getCost() * 10), n.getLoc());
}
else
{
// Known nodes.
dropDebugItem(57, (int) (n.getCost() * 10), n.getLoc());
}
}
}
if (result == null)
{
_findFails++;
return null;
}
path = constructPath(result);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
return null;
}
finally
{
buffer.free();
}
if ((path.size() < 3) || (Config.MAX_POSTFILTER_PASSES <= 0))
{
_findSuccess++;
return path;
}
final long timeStamp = System.currentTimeMillis();
_postFilterUses++;
if (playable)
{
_postFilterPlayableUses++;
}
boolean remove;
int pass = 0;
do
{
pass++;
_postFilterPasses++;
remove = false;
final Iterator<AbstractNodeLoc> endPoint = path.iterator();
endPoint.next();
int currentX = x;
int currentY = y;
int currentZ = z;
int midPoint = 0;
while (endPoint.hasNext())
{
final AbstractNodeLoc locMiddle = path.get(midPoint);
final AbstractNodeLoc locEnd = endPoint.next();
if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance))
{
path.remove(midPoint);
remove = true;
if (debug)
{
dropDebugItem(735, 1, locMiddle);
}
}
else
{
currentX = locMiddle.getX();
currentY = locMiddle.getY();
currentZ = locMiddle.getZ();
midPoint++;
}
}
}
// Only one postfilter pass for AI.
while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES));
if (debug)
{
path.forEach(n -> dropDebugItem(65, 1, n));
}
_findSuccess++;
_postFilterElapsed += System.currentTimeMillis() - timeStamp;
return path;
}
private List<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> node)
{
final List<AbstractNodeLoc> path = new CopyOnWriteArrayList<>();
int previousDirectionX = Integer.MIN_VALUE;
int previousDirectionY = Integer.MIN_VALUE;
int directionX;
int directionY;
AbstractNode<NodeLoc> tempNode = node;
while (tempNode.getParent() != null)
{
if (!Config.ADVANCED_DIAGONAL_STRATEGY && (tempNode.getParent().getParent() != null))
{
final int tmpX = tempNode.getLoc().getNodeX() - tempNode.getParent().getParent().getLoc().getNodeX();
final int tmpY = tempNode.getLoc().getNodeY() - tempNode.getParent().getParent().getLoc().getNodeY();
if (Math.abs(tmpX) == Math.abs(tmpY))
{
directionX = tmpX;
directionY = tmpY;
}
else
{
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
}
}
else
{
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
}
// Only add a new route point if moving direction changes.
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.add(0, tempNode.getLoc());
tempNode.setLoc(null);
}
tempNode = tempNode.getParent();
}
return path;
}
private final CellNodeBuffer alloc(int size, boolean playable)
{
CellNodeBuffer current = null;
for (BufferInfo i : _allBuffers)
{
if (i.mapSize >= size)
{
for (CellNodeBuffer buf : i.bufs)
{
if (buf.lock())
{
i.uses++;
if (playable)
{
i.playableUses++;
}
i.elapsed += buf.getElapsedTime();
current = buf;
break;
}
}
if (current != null)
{
break;
}
// Not found, allocate temporary buffer.
current = new CellNodeBuffer(i.mapSize);
current.lock();
if (i.bufs.size() < i.count)
{
i.bufs.add(current);
i.uses++;
if (playable)
{
i.playableUses++;
}
break;
}
i.overflows++;
if (playable)
{
i.playableOverflows++;
// System.err.println("Overflow, size requested: " + size + " playable:"+playable);
}
}
}
return current;
}
private final void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
{
final Item item = new Item(IdManager.getInstance().getNextId(), itemId);
item.setCount(num);
item.spawnMe(loc.getX(), loc.getY(), loc.getZ());
_debugItems.add(item);
}
private static final class BufferInfo
{
final int mapSize;
final int count;
List<CellNodeBuffer> bufs;
int uses = 0;
int playableUses = 0;
int overflows = 0;
int playableOverflows = 0;
long elapsed = 0;
public BufferInfo(int size, int cnt)
{
mapSize = size;
count = cnt;
bufs = new ArrayList<>(count);
}
@Override
public String toString()
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat, String.valueOf(mapSize), "x", String.valueOf(mapSize), " num:", String.valueOf(bufs.size()), "/", String.valueOf(count), " uses:", String.valueOf(uses), "/", String.valueOf(playableUses));
if (uses > 0)
{
StringUtil.append(stat, " total/avg(ms):", String.valueOf(elapsed), "/", String.format("%1.2f", (double) elapsed / uses));
}
StringUtil.append(stat, " ovf:", String.valueOf(overflows), "/", String.valueOf(playableOverflows));
return stat.toString();
}
}
@Override
public String[] getStat()
{
final String[] result = new String[_allBuffers.length + 1];
for (int i = 0; i < _allBuffers.length; i++)
{
result[i] = _allBuffers[i].toString();
}
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat, "LOS postfilter uses:", String.valueOf(_postFilterUses), "/", String.valueOf(_postFilterPlayableUses));
if (_postFilterUses > 0)
{
StringUtil.append(stat, " total/avg(ms):", String.valueOf(_postFilterElapsed), "/", String.format("%1.2f", (double) _postFilterElapsed / _postFilterUses), " passes total/avg:", String.valueOf(_postFilterPasses), "/", String.format("%1.1f", (double) _postFilterPasses / _postFilterUses), Config.EOL);
}
StringUtil.append(stat, "Pathfind success/fail:", String.valueOf(_findSuccess), "/", String.valueOf(_findFails));
result[result.length - 1] = stat.toString();
return result;
}
public static CellPathFinding getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final CellPathFinding INSTANCE = new CellPathFinding();
}
}

View File

@@ -0,0 +1,165 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.Cell;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
/**
* @author -Nemesiss-, HorridoJoho
*/
public class NodeLoc extends AbstractNodeLoc
{
private int _x;
private int _y;
private boolean _goNorth;
private boolean _goEast;
private boolean _goSouth;
private boolean _goWest;
private int _geoHeight;
public NodeLoc(int x, int y, int z)
{
set(x, y, z);
}
public void set(int x, int y, int z)
{
_x = x;
_y = y;
_goNorth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH);
_goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST);
_goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH);
_goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST);
_geoHeight = GeoEngine.getInstance().getNearestZ(x, y, z);
}
public boolean canGoNorth()
{
return _goNorth;
}
public boolean canGoEast()
{
return _goEast;
}
public boolean canGoSouth()
{
return _goSouth;
}
public boolean canGoWest()
{
return _goWest;
}
public boolean canGoNone()
{
return !canGoNorth() && !canGoEast() && !canGoSouth() && !canGoWest();
}
public boolean canGoAll()
{
return canGoNorth() && canGoEast() && canGoSouth() && canGoWest();
}
@Override
public int getX()
{
return GeoEngine.getInstance().getWorldX(_x);
}
@Override
public int getY()
{
return GeoEngine.getInstance().getWorldY(_y);
}
@Override
public int getZ()
{
return _geoHeight;
}
@Override
public void setZ(short z)
{
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
int nswe = 0;
if (canGoNorth())
{
nswe |= Cell.NSWE_NORTH;
}
if (canGoEast())
{
nswe |= Cell.NSWE_EAST;
}
if (canGoSouth())
{
nswe |= Cell.NSWE_SOUTH;
}
if (canGoWest())
{
nswe |= Cell.NSWE_WEST;
}
result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe);
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof NodeLoc))
{
return false;
}
final NodeLoc other = (NodeLoc) obj;
return (_x == other._x) && (_y == other._y) && (!_goNorth == !other._goNorth) && (!_goEast == !other._goEast) && (!_goSouth == !other._goSouth) && (!_goWest == !other._goWest) && (_geoHeight == other._geoHeight);
}
}

View File

@@ -0,0 +1,60 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class GeoNode extends AbstractNode<GeoNodeLoc>
{
private final int _neighborsIdx;
private short _cost;
private GeoNode[] _neighbors;
public GeoNode(GeoNodeLoc loc, int neighborsIdx)
{
super(loc);
_neighborsIdx = neighborsIdx;
}
public short getCost()
{
return _cost;
}
public void setCost(int cost)
{
_cost = (short) cost;
}
public GeoNode[] getNeighbors()
{
return _neighbors;
}
public void attachNeighbors(GeoNode[] neighbors)
{
_neighbors = neighbors;
}
public int getNeighborsIdx()
{
return _neighborsIdx;
}
}

View File

@@ -0,0 +1,102 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.model.World;
/**
* @author -Nemesiss-
*/
public class GeoNodeLoc extends AbstractNodeLoc
{
private final short _x;
private final short _y;
private final short _z;
public GeoNodeLoc(short x, short y, short z)
{
_x = x;
_y = y;
_z = z;
}
@Override
public int getX()
{
return World.WORLD_X_MIN + (_x * 128) + 48;
}
@Override
public int getY()
{
return World.WORLD_Y_MIN + (_y * 128) + 48;
}
@Override
public int getZ()
{
return _z;
}
@Override
public void setZ(short z)
{
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
result = (prime * result) + _z;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof GeoNodeLoc))
{
return false;
}
final GeoNodeLoc other = (GeoNodeLoc) obj;
return (_x == other._x) && (_y == other._y) && (_z == other._z);
}
}

View File

@@ -0,0 +1,445 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.utils.FastNodeList;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author -Nemesiss-
*/
public class GeoPathFinding extends PathFinding
{
private static final Logger LOGGER = Logger.getLogger(GeoPathFinding.class.getName());
private static final Map<Short, ByteBuffer> PATH_NODES = new HashMap<>();
private static final Map<Short, IntBuffer> PATH_NODE_INDEX = new HashMap<>();
@Override
public boolean pathNodesExist(short regionoffset)
{
return PATH_NODE_INDEX.containsKey(regionoffset);
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
{
final int gx = (x - World.WORLD_X_MIN) >> 4;
final int gy = (y - World.WORLD_Y_MIN) >> 4;
final short gz = (short) z;
final int gtx = (tx - World.WORLD_X_MIN) >> 4;
final int gty = (ty - World.WORLD_Y_MIN) >> 4;
final short gtz = (short) tz;
final GeoNode start = readNode(gx, gy, gz);
final GeoNode end = readNode(gtx, gty, gtz);
if ((start == null) || (end == null))
{
return null;
}
if (Math.abs(start.getLoc().getZ() - z) > 55)
{
return null; // Not correct layer.
}
if (Math.abs(end.getLoc().getZ() - tz) > 55)
{
return null; // Not correct layer.
}
if (start == end)
{
return null;
}
// TODO: Find closest path node we CAN access. Now only checks if we can not reach the closest.
Location temp = GeoEngine.getInstance().getValidLocation(x, y, z, start.getLoc().getX(), start.getLoc().getY(), start.getLoc().getZ(), instance);
if ((temp.getX() != start.getLoc().getX()) || (temp.getY() != start.getLoc().getY()))
{
return null; // Cannot reach closest...
}
// TODO: Find closest path node around target, now only checks if final location can be reached.
temp = GeoEngine.getInstance().getValidLocation(tx, ty, tz, end.getLoc().getX(), end.getLoc().getY(), end.getLoc().getZ(), instance);
if ((temp.getX() != end.getLoc().getX()) || (temp.getY() != end.getLoc().getY()))
{
return null; // Cannot reach closest...
}
return searchByClosest2(start, end);
}
public List<AbstractNodeLoc> searchByClosest2(GeoNode start, GeoNode end)
{
// Always continues checking from the closest to target non-blocked
// node from to_visit list. There's extra length in path if needed
// to go backwards/sideways but when moving generally forwards, this is extra fast
// and accurate. And can reach insane distances (try it with 800 nodes..).
// Minimum required node count would be around 300-400.
// Generally returns a bit (only a bit) more intelligent looking routes than
// the basic version. Not a true distance image (which would increase CPU
// load) level of intelligence though.
// List of Visited Nodes.
final FastNodeList visited = new FastNodeList(550);
// List of Nodes to Visit.
final LinkedList<GeoNode> toVisit = new LinkedList<>();
toVisit.add(start);
final int targetX = end.getLoc().getNodeX();
final int targetY = end.getLoc().getNodeY();
int dx, dy;
boolean added;
int i = 0;
while (i < 550)
{
GeoNode node;
try
{
node = toVisit.removeFirst();
}
catch (Exception e)
{
// No Path found
return null;
}
if (node.equals(end))
{
return constructPath2(node);
}
i++;
visited.add(node);
node.attachNeighbors(readNeighbors(node));
final GeoNode[] neighbors = node.getNeighbors();
if (neighbors == null)
{
continue;
}
for (GeoNode n : neighbors)
{
if (!visited.containsRev(n) && !toVisit.contains(n))
{
added = false;
n.setParent(node);
dx = targetX - n.getLoc().getNodeX();
dy = targetY - n.getLoc().getNodeY();
n.setCost((dx * dx) + (dy * dy));
for (int index = 0; index < toVisit.size(); index++)
{
// Supposed to find it quite early.
if (toVisit.get(index).getCost() > n.getCost())
{
toVisit.add(index, n);
added = true;
break;
}
}
if (!added)
{
toVisit.addLast(n);
}
}
}
}
// No Path found.
return null;
}
public List<AbstractNodeLoc> constructPath2(AbstractNode<GeoNodeLoc> node)
{
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
int previousDirectionX = -1000;
int previousDirectionY = -1000;
int directionX;
int directionY;
AbstractNode<GeoNodeLoc> tempNode = node;
while (tempNode.getParent() != null)
{
// Only add a new route point if moving direction changes.
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.addFirst(tempNode.getLoc());
}
tempNode = tempNode.getParent();
}
return path;
}
private GeoNode[] readNeighbors(GeoNode n)
{
if (n.getLoc() == null)
{
return null;
}
int idx = n.getNeighborsIdx();
final int nodeX = n.getLoc().getNodeX();
final int nodeY = n.getLoc().getNodeY();
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
final ByteBuffer pn = PATH_NODES.get(regoffset);
final List<AbstractNode<GeoNodeLoc>> neighbors = new ArrayList<>(8);
GeoNode newNode;
short newNodeX;
short newNodeY;
// Region for sure will change, we must read from correct file
byte neighbor = pn.get(idx++); // N
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) nodeX;
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NE
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // E
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) nodeY;
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SE
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // S
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) nodeX;
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SW
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // W
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) nodeY;
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NW
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
return neighbors.toArray(new GeoNode[neighbors.size()]);
}
// Private
private GeoNode readNode(short nodeX, short nodeY, byte layer)
{
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(nodeX);
final short nby = getNodeBlock(nodeY);
int idx = PATH_NODE_INDEX.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = PATH_NODES.get(regoffset);
// Reading.
final byte nodes = pn.get(idx);
idx += (layer * 10) + 1; // byte + layer*10byte
if (nodes < layer)
{
LOGGER.warning("SmthWrong!");
}
final short node_z = pn.getShort(idx);
idx += 2;
return new GeoNode(new GeoNodeLoc(nodeX, nodeY, node_z), idx);
}
private GeoNode readNode(int gx, int gy, short z)
{
final short nodeX = getNodePos(gx);
final short nodeY = getNodePos(gy);
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(nodeX);
final short nby = getNodeBlock(nodeY);
int idx = PATH_NODE_INDEX.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = PATH_NODES.get(regoffset);
// Reading.
byte nodes = pn.get(idx++);
int idx2 = 0; // Create index to nearlest node by z.
short lastZ = Short.MIN_VALUE;
while (nodes > 0)
{
final short node_z = pn.getShort(idx);
if (Math.abs(lastZ - z) > Math.abs(node_z - z))
{
lastZ = node_z;
idx2 = idx + 2;
}
idx += 10; // short + 8 byte
nodes--;
}
return new GeoNode(new GeoNodeLoc(nodeX, nodeY, lastZ), idx2);
}
protected GeoPathFinding()
{
for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++)
{
for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++)
{
loadPathNodeFile((byte) regionX, (byte) regionY);
}
}
}
private void loadPathNodeFile(byte rx, byte ry)
{
final short regionoffset = getRegionOffset(rx, ry);
final File file = new File(Config.PATHNODE_PATH.toString(), rx + "_" + ry + ".pn");
if (!file.exists())
{
return;
}
// LOGGER.info("Path Engine: - Loading: " + file.getName() + " -> region offset: " + regionoffset + " X: " + rx + " Y: " + ry);
int node = 0;
int size = 0;
int index = 0;
// Create a read-only memory-mapped file.
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel roChannel = raf.getChannel())
{
size = (int) roChannel.size();
final MappedByteBuffer nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
// Indexing pathnode files, so we will know where each block starts.
final IntBuffer indexs = IntBuffer.allocate(65536);
while (node < 65536)
{
final byte layer = nodes.get(index);
indexs.put(node++, index);
index += (layer * 10) + 1;
}
PATH_NODE_INDEX.put(regionoffset, indexs);
PATH_NODES.put(regionoffset, nodes);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to Load PathNode File: " + file.getAbsolutePath() + " : " + e.getMessage(), e);
}
}
public static GeoPathFinding getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final GeoPathFinding INSTANCE = new GeoPathFinding();
}
}

View File

@@ -0,0 +1,124 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.utils;
import org.l2jmobius.gameserver.geoengine.pathfinding.geonodes.GeoNode;
/**
* @author -Nemesiss-
*/
public class BinaryNodeHeap
{
private final GeoNode[] _list;
private int _size;
public BinaryNodeHeap(int size)
{
_list = new GeoNode[size + 1];
_size = 0;
}
public void add(GeoNode n)
{
_size++;
int pos = _size;
_list[pos] = n;
while (pos != 1)
{
final int p2 = pos / 2;
if (_list[pos].getCost() <= _list[p2].getCost())
{
final GeoNode temp = _list[p2];
_list[p2] = _list[pos];
_list[pos] = temp;
pos = p2;
}
else
{
break;
}
}
}
public GeoNode removeFirst()
{
final GeoNode first = _list[1];
_list[1] = _list[_size];
_list[_size] = null;
_size--;
int pos = 1;
int cpos;
int dblcpos;
GeoNode temp;
while (true)
{
cpos = pos;
dblcpos = cpos * 2;
if ((dblcpos + 1) <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
if (_list[pos].getCost() >= _list[dblcpos + 1].getCost())
{
pos = dblcpos + 1;
}
}
else if (dblcpos <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
}
if (cpos != pos)
{
temp = _list[cpos];
_list[cpos] = _list[pos];
_list[pos] = temp;
}
else
{
break;
}
}
return first;
}
public boolean contains(GeoNode n)
{
if (_size == 0)
{
return false;
}
for (int i = 1; i <= _size; i++)
{
if (_list[i].equals(n))
{
return true;
}
}
return false;
}
public boolean isEmpty()
{
return _size == 0;
}
}

View File

@@ -0,0 +1,49 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.utils;
import java.util.ArrayList;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class FastNodeList
{
private final ArrayList<AbstractNode<?>> _list;
public FastNodeList(int size)
{
_list = new ArrayList<>(size);
}
public void add(AbstractNode<?> n)
{
_list.add(n);
}
public boolean contains(AbstractNode<?> n)
{
return _list.contains(n);
}
public boolean containsRev(AbstractNode<?> n)
{
return _list.lastIndexOf(n) != -1;
}
}

View File

@@ -400,7 +400,7 @@ public class Spawn extends Location implements IIdentifiable, INamable
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE); final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE); final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
if (GeoEngine.getInstance().canMoveToTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()) // if (GeoEngine.getInstance().canMoveToTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()) //
&& GeoEngine.getInstance().canSee(newlocx, newlocy, newlocz, npc.getCollisionHeight(), randX, randY, newlocz, 0)) && GeoEngine.getInstance().canSeeTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()))
{ {
newlocx = randX; newlocx = randX;
newlocy = randY; newlocy = randY;

View File

@@ -67,6 +67,8 @@ import org.l2jmobius.gameserver.enums.Team;
import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.TeleportWhereType;
import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.enums.UserInfoType;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.IdManager;
import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager;
import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.QuestManager;
@@ -821,7 +823,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
{ {
int x = xValue; int x = xValue;
int y = yValue; int y = yValue;
int z = zValue; int z = _isFlying ? zValue : GeoEngine.getInstance().getHeight(x, y, zValue);
int heading = headingValue; int heading = headingValue;
Instance instance = instanceValue; Instance instance = instanceValue;
@@ -2688,7 +2690,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
public boolean disregardingGeodata; public boolean disregardingGeodata;
public int onGeodataPathIndex; public int onGeodataPathIndex;
public List<Location> geoPath; public List<AbstractNodeLoc> geoPath;
public int geoPathAccurateTx; public int geoPathAccurateTx;
public int geoPathAccurateTy; public int geoPathAccurateTy;
public int geoPathGtx; public int geoPathGtx;
@@ -3465,7 +3467,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
} }
// Movement checks. // Movement checks.
if (Config.PATHFINDING && !(this instanceof FriendlyNpc)) if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
{ {
final double originalDistance = distance; final double originalDistance = distance;
final int originalX = x; final int originalX = x;
@@ -3509,7 +3511,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle)
{ {
// Path calculation -- overrides previous movement check // Path calculation -- overrides previous movement check
m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
{ {
if (isPlayer() && !_isFlying && !isInWater) if (isPlayer() && !_isFlying && !isInWater)
@@ -3553,7 +3555,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
} }
// If no distance to go through, the movement is canceled // If no distance to go through, the movement is canceled
if ((distance < 1) && (Config.PATHFINDING || isPlayable())) if ((distance < 1) && ((Config.PATHFINDING > 0) || isPlayable()))
{ {
if (isSummon()) if (isSummon())
{ {

View File

@@ -19,7 +19,7 @@ package org.l2jmobius.gameserver.util;
import java.awt.Color; import java.awt.Color;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.geoengine.geodata.Cell;
import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive;
@@ -30,21 +30,21 @@ public final class GeoUtils
{ {
public static void debug2DLine(Player player, int x, int y, int tx, int ty, int z) public static void debug2DLine(Player player, int x, int y, int tx, int ty, int z)
{ {
final int gx = GeoEngine.getGeoX(x); final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getGeoY(y); final int gy = GeoEngine.getInstance().getGeoY(y);
final int tgx = GeoEngine.getGeoX(tx); final int tgx = GeoEngine.getInstance().getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty); final int tgy = GeoEngine.getInstance().getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z);
final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy);
while (iter.next()) while (iter.next())
{ {
final int wx = GeoEngine.getWorldX(iter.x()); final int wx = GeoEngine.getInstance().getWorldX(iter.x());
final int wy = GeoEngine.getWorldY(iter.y()); final int wy = GeoEngine.getInstance().getWorldY(iter.y());
prim.addPoint(Color.RED, wx, wy, z); prim.addPoint(Color.RED, wx, wy, z);
} }
@@ -53,21 +53,21 @@ public final class GeoUtils
public static void debug3DLine(Player player, int x, int y, int z, int tx, int ty, int tz) public static void debug3DLine(Player player, int x, int y, int z, int tx, int ty, int tz)
{ {
final int gx = GeoEngine.getGeoX(x); final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getGeoY(y); final int gy = GeoEngine.getInstance().getGeoY(y);
final int tgx = GeoEngine.getGeoX(tx); final int tgx = GeoEngine.getInstance().getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty); final int tgy = GeoEngine.getInstance().getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz);
final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz); final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz);
iter.next(); iter.next();
int prevX = iter.x(); int prevX = iter.x();
int prevY = iter.y(); int prevY = iter.y();
int wx = GeoEngine.getWorldX(prevX); int wx = GeoEngine.getInstance().getWorldX(prevX);
int wy = GeoEngine.getWorldY(prevY); int wy = GeoEngine.getInstance().getWorldY(prevY);
int wz = iter.z(); int wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz); prim.addPoint(Color.RED, wx, wy, wz);
@@ -78,8 +78,8 @@ public final class GeoUtils
if ((curX != prevX) || (curY != prevY)) if ((curX != prevX) || (curY != prevY))
{ {
wx = GeoEngine.getWorldX(curX); wx = GeoEngine.getInstance().getWorldX(curX);
wy = GeoEngine.getWorldY(curY); wy = GeoEngine.getInstance().getWorldY(curY);
wz = iter.z(); wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz); prim.addPoint(Color.RED, wx, wy, wz);
@@ -93,7 +93,7 @@ public final class GeoUtils
private static Color getDirectionColor(int x, int y, int z, int nswe) private static Color getDirectionColor(int x, int y, int z, int nswe)
{ {
if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe))
{ {
return Color.GREEN; return Color.GREEN;
} }
@@ -109,8 +109,9 @@ public final class GeoUtils
int iPacket = 0; int iPacket = 0;
ExServerPrimitive exsp = null; ExServerPrimitive exsp = null;
final int playerGx = GeoEngine.getGeoX(player.getX()); final GeoEngine ge = GeoEngine.getInstance();
final int playerGy = GeoEngine.getGeoY(player.getY()); final int playerGx = ge.getGeoX(player.getX());
final int playerGy = ge.getGeoY(player.getY());
for (int dx = -geoRadius; dx <= geoRadius; ++dx) for (int dx = -geoRadius; dx <= geoRadius; ++dx)
{ {
for (int dy = -geoRadius; dy <= geoRadius; ++dy) for (int dy = -geoRadius; dy <= geoRadius; ++dy)
@@ -134,32 +135,32 @@ public final class GeoUtils
final int gx = playerGx + dx; final int gx = playerGx + dx;
final int gy = playerGy + dy; final int gy = playerGy + dy;
final int x = GeoEngine.getWorldX(gx); final int x = ge.getWorldX(gx);
final int y = GeoEngine.getWorldY(gy); final int y = ge.getWorldY(gy);
final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); final int z = ge.getNearestZ(gx, gy, player.getZ());
// north arrow // north arrow
Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH);
exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z); exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z);
exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z); exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z);
exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z); exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z);
exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z);
// east arrow // east arrow
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST);
exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z); exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z);
exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z); exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z);
exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z); exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z);
exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z);
// south arrow // south arrow
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH);
exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z); exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z);
exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z); exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z);
exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z); exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z);
exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z); exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z);
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST);
exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z); exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z);
exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z); exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z);
exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z); exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z);
@@ -187,41 +188,41 @@ public final class GeoUtils
{ {
if (y > lastY) if (y > lastY)
{ {
return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; return Cell.NSWE_SOUTH_EAST;
} }
else if (y < lastY) else if (y < lastY)
{ {
return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; return Cell.NSWE_NORTH_EAST;
} }
else else
{ {
return GeoStructure.CELL_FLAG_E; return Cell.NSWE_EAST;
} }
} }
else if (x < lastX) // west else if (x < lastX) // west
{ {
if (y > lastY) if (y > lastY)
{ {
return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; return Cell.NSWE_SOUTH_WEST;
} }
else if (y < lastY) else if (y < lastY)
{ {
return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; return Cell.NSWE_NORTH_WEST;
} }
else else
{ {
return GeoStructure.CELL_FLAG_W; return Cell.NSWE_WEST;
} }
} }
else // unchanged x else // unchanged x
{ {
if (y > lastY) if (y > lastY)
{ {
return GeoStructure.CELL_FLAG_S; return Cell.NSWE_SOUTH;
} }
else if (y < lastY) else if (y < lastY)
{ {
return GeoStructure.CELL_FLAG_N; return Cell.NSWE_NORTH;
} }
else else
{ {

View File

@@ -298,7 +298,6 @@ CorrectPrices = True
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Allow characters to receive damage from falling. # Allow characters to receive damage from falling.
# CoordSynchronize = 2 is recommended.
# Default: True # Default: True
EnableFallingDamage = True EnableFallingDamage = True

View File

@@ -2,48 +2,47 @@
# Geodata # Geodata
# ================================================================= # =================================================================
# Specifies the path to geodata files. For example, when using geodata files located # Pathfinding options:
# at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ # 0 = Disabled
# 1 = Enabled using path node files.
# 2 = Enabled using geodata cells at runtime.
# Default: 0
PathFinding = 2
# Geodata file directory.
GeoDataPath = ./data/geodata/ GeoDataPath = ./data/geodata/
# Specifies the geodata files type. Default: L2J # Pathnode file directory.
# L2J: Using L2J geodata files (filename e.g. 22_16.l2j) # Default: pathnode
# L2OFF: Using L2OFF geodata files (filename e.g. 22_16_conv.dat) PathnodePath = ./data/pathnode/
GeoDataType = L2J
# ================================================================= # Pathfinding array buffers configuration.
# Pathfinding PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
# =================================================================
# When line of movement check fails, the pathfinding algoritm is performed to look for # Weight for nodes without obstacles far from walls.
# an alternative path (e.g. walk around obstacle), default: True LowWeight = 0.5
PathFinding = True
# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 # Weight for nodes near walls.
PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 MediumWeight = 2
# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 # Weight for nodes with obstacles.
MoveWeight = 10 HighWeight = 3
MoveWeightDiag = 14
# When movement flags of target node is blocked to any direction, use this weight instead of MoveWeight or MoveWeightDiag. # Angle paths will be more "smart", but in cost of higher CPU utilization.
# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 30 AdvancedDiagonalStrategy = True
ObstacleWeight = 30
# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 12 and 18 # Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True
# For proper function must be higher than MoveWeight. # Default: LowWeight * sqrt(2)
HeuristicWeight = 12 DiagonalWeight = 0.707
HeuristicWeightDiag = 18
# Maximum number of generated nodes per one path-finding process, default 3500 # Maximum number of LOS postfilter passes, 0 will disable postfilter.
MaxIterations = 3500 # Default: 3
MaxPostfilterPasses = 3
# ================================================================= # Path debug function.
# Line of Sight # Nodes known to pathfinder will be displayed as adena, constructed path as antidots.
# ================================================================= # Number of the items show node cost * 10
# Potions display path after first stage filter
# Line of sight start at X percent of the character height, default: 75 # Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter
PartOfCharacterHeight = 75 # This function FOR DEBUG PURPOSES ONLY, never use it on the live server!
DebugPath = False
# Maximum height of an obstacle, which can exceed the line of sight, default: 32
MaxObstacleHeight = 32

View File

@@ -25,6 +25,3 @@ b - Make it work
To make geodata work: To make geodata work:
* unpack your geodata files into "/data/geodata" folder (or any other folder) * unpack your geodata files into "/data/geodata" folder (or any other folder)
* open "/config/GeoEngine.ini" with your favorite text editor and then edit following configs:
- GeoDataPath = set path to your geodata, if elsewhere than "./data/geodata/"
- GeoDataType = set the geodata format, which you are using.

View File

@@ -55,8 +55,8 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX(); final int worldX = activeChar.getX();
final int worldY = activeChar.getY(); final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ(); final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX); final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY); final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{ {
@@ -73,8 +73,8 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX(); final int worldX = activeChar.getX();
final int worldY = activeChar.getY(); final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ(); final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX); final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY); final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{ {

View File

@@ -19,9 +19,9 @@ package handlers.admincommandhandlers;
import java.util.List; import java.util.List;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler; import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.util.BuilderUtil; import org.l2jmobius.gameserver.util.BuilderUtil;
@@ -37,20 +37,21 @@ public class AdminPathNode implements IAdminCommandHandler
{ {
if (command.equals("admin_path_find")) if (command.equals("admin_path_find"))
{ {
if (!Config.PATHFINDING) if (Config.PATHFINDING < 1)
{ {
BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled.");
return true; return true;
} }
if (activeChar.getTarget() != null) if (activeChar.getTarget() != null)
{ {
final List<Location> path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); final List<AbstractNodeLoc> path = PathFinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld(), true);
if (path == null) if (path == null)
{ {
BuilderUtil.sendSysMessage(activeChar, "No Route!"); BuilderUtil.sendSysMessage(activeChar, "No Route!");
return true; return true;
} }
for (Location a : path) for (AbstractNodeLoc a : path)
{ {
BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ());
} }

View File

@@ -72,7 +72,7 @@ public class AdminServerInfo implements IAdminCommandHandler
html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS); html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS);
html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute()); html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute());
html.replace("%dayNight%", GameTimeTaskManager.getInstance().isNight() ? "Night" : "Day"); html.replace("%dayNight%", GameTimeTaskManager.getInstance().isNight() ? "Night" : "Day");
html.replace("%geodata%", Config.PATHFINDING ? "Enabled" : "Disabled"); html.replace("%geodata%", Config.PATHFINDING > 0 ? "Enabled" : "Disabled");
html.replace("%serverTime%", SDF.format(new Date(System.currentTimeMillis()))); html.replace("%serverTime%", SDF.format(new Date(System.currentTimeMillis())));
html.replace("%serverUpTime%", getServerUpTime()); html.replace("%serverUpTime%", getServerUpTime());
html.replace("%onlineAll%", getPlayersCount("ALL")); html.replace("%onlineAll%", getPlayersCount("ALL"));

View File

@@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler
return null; return null;
} }
if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition))
{ {
if (sendMessage) if (sendMessage)
{ {

View File

@@ -61,7 +61,6 @@ import org.l2jmobius.commons.util.PropertiesParser;
import org.l2jmobius.commons.util.StringUtil; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.enums.ClassId; import org.l2jmobius.gameserver.enums.ClassId;
import org.l2jmobius.gameserver.enums.GeoType;
import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType; import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType;
import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder;
@@ -954,18 +953,16 @@ public class Config
// GeoEngine // GeoEngine
// -------------------------------------------------- // --------------------------------------------------
public static Path GEODATA_PATH; public static Path GEODATA_PATH;
public static GeoType GEODATA_TYPE; public static Path PATHNODE_PATH;
public static boolean PATHFINDING; public static int PATHFINDING;
public static String PATHFIND_BUFFERS; public static String PATHFIND_BUFFERS;
public static int MOVE_WEIGHT; public static float LOW_WEIGHT;
public static int MOVE_WEIGHT_DIAG; public static float MEDIUM_WEIGHT;
public static int OBSTACLE_WEIGHT; public static float HIGH_WEIGHT;
public static int OBSTACLE_WEIGHT_DIAG; public static boolean ADVANCED_DIAGONAL_STRATEGY;
public static int HEURISTIC_WEIGHT; public static float DIAGONAL_WEIGHT;
public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_POSTFILTER_PASSES;
public static int MAX_ITERATIONS; public static boolean DEBUG_PATH;
public static int PART_OF_CHARACTER_HEIGHT;
public static int MAX_OBSTACLE_HEIGHT;
/** Attribute System */ /** Attribute System */
public static int S_WEAPON_STONE; public static int S_WEAPON_STONE;
@@ -2503,19 +2500,17 @@ public class Config
// Load GeoEngine config file (if exists) // Load GeoEngine config file (if exists)
final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE);
GEODATA_PATH = Paths.get(geoEngineConfig.getString("GeoDataPath", "./data/geodata")); GEODATA_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("GeoDataPath", "geodata"));
GEODATA_TYPE = Enum.valueOf(GeoType.class, geoEngineConfig.getString("GeoDataType", "L2J")); PATHNODE_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("PathnodePath", "pathnode"));
PATHFINDING = geoEngineConfig.getBoolean("PathFinding", true); PATHFINDING = geoEngineConfig.getInt("PathFinding", 0);
PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
MOVE_WEIGHT = geoEngineConfig.getInt("MoveWeight", 10); LOW_WEIGHT = geoEngineConfig.getFloat("LowWeight", 0.5f);
MOVE_WEIGHT_DIAG = geoEngineConfig.getInt("MoveWeightDiag", 14); MEDIUM_WEIGHT = geoEngineConfig.getFloat("MediumWeight", 2);
OBSTACLE_WEIGHT = geoEngineConfig.getInt("ObstacleWeight", 30); HIGH_WEIGHT = geoEngineConfig.getFloat("HighWeight", 3);
OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); ADVANCED_DIAGONAL_STRATEGY = geoEngineConfig.getBoolean("AdvancedDiagonalStrategy", true);
HEURISTIC_WEIGHT = geoEngineConfig.getInt("HeuristicWeight", 12); DIAGONAL_WEIGHT = geoEngineConfig.getFloat("DiagonalWeight", 0.707f);
HEURISTIC_WEIGHT_DIAG = geoEngineConfig.getInt("HeuristicWeightDiag", 18); MAX_POSTFILTER_PASSES = geoEngineConfig.getInt("MaxPostfilterPasses", 3);
MAX_ITERATIONS = geoEngineConfig.getInt("MaxIterations", 3500); DEBUG_PATH = geoEngineConfig.getBoolean("DebugPath", false);
PART_OF_CHARACTER_HEIGHT = geoEngineConfig.getInt("PartOfCharacterHeight", 75);
MAX_OBSTACLE_HEIGHT = geoEngineConfig.getInt("MaxObstacleHeight", 32);
// Load AllowedPlayerRaces config file (if exists) // Load AllowedPlayerRaces config file (if exists)
final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE);

View File

@@ -1,144 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.enums;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
/**
* Container of movement constants used for various geodata and movement checks.
*/
public enum MoveDirectionType
{
N(0, -1),
S(0, 1),
W(-1, 0),
E(1, 0),
NW(-1, -1),
SW(-1, 1),
NE(1, -1),
SE(1, 1);
// Step and signum.
private final int _stepX;
private final int _stepY;
private final int _signumX;
private final int _signumY;
// Cell offset.
private final int _offsetX;
private final int _offsetY;
// Direction flags.
private final byte _directionX;
private final byte _directionY;
private final String _symbolX;
private final String _symbolY;
private MoveDirectionType(int signumX, int signumY)
{
// Get step (world -16, 0, 16) and signum (geodata -1, 0, 1) coordinates.
_stepX = signumX * GeoStructure.CELL_SIZE;
_stepY = signumY * GeoStructure.CELL_SIZE;
_signumX = signumX;
_signumY = signumY;
// Get border offsets in a direction of iteration.
_offsetX = signumX >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
_offsetY = signumY >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
// Get direction NSWE flag and symbol.
_directionX = signumX < 0 ? GeoStructure.CELL_FLAG_W : signumX == 0 ? 0 : GeoStructure.CELL_FLAG_E;
_directionY = signumY < 0 ? GeoStructure.CELL_FLAG_N : signumY == 0 ? 0 : GeoStructure.CELL_FLAG_S;
_symbolX = signumX < 0 ? "W" : signumX == 0 ? "-" : "E";
_symbolY = signumY < 0 ? "N" : signumY == 0 ? "-" : "S";
}
public int getStepX()
{
return _stepX;
}
public int getStepY()
{
return _stepY;
}
public int getSignumX()
{
return _signumX;
}
public int getSignumY()
{
return _signumY;
}
public int getOffsetX()
{
return _offsetX;
}
public int getOffsetY()
{
return _offsetY;
}
public byte getDirectionX()
{
return _directionX;
}
public byte getDirectionY()
{
return _directionY;
}
public String getSymbolX()
{
return _symbolX;
}
public String getSymbolY()
{
return _symbolY;
}
/**
* @param gdx : Geodata X delta coordinate.
* @param gdy : Geodata Y delta coordinate.
* @return {@link MoveDirectionType} based on given geodata dx and dy delta coordinates.
*/
public static MoveDirectionType getDirection(int gdx, int gdy)
{
if (gdx == 0)
{
return (gdy < 0) ? MoveDirectionType.N : MoveDirectionType.S;
}
if (gdy == 0)
{
return (gdx < 0) ? MoveDirectionType.W : MoveDirectionType.E;
}
if (gdx > 0)
{
return (gdy < 0) ? MoveDirectionType.NE : MoveDirectionType.SE;
}
return (gdy < 0) ? MoveDirectionType.NW : MoveDirectionType.SW;
}
}

View File

@@ -1,85 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
public abstract class ABlock
{
/**
* Checks the block for having geodata.
* @return boolean : True, when block has geodata (Flat, Complex, Multilayer).
*/
public abstract boolean hasGeoPos();
/**
* Returns the height of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, nearest to given coordinates.
*/
public abstract short getHeightNearest(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is closes layer to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index.
*/
public abstract int getIndexNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer above given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexAbove(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer below given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexBelow(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeight(int index);
/**
* Returns the NSWE flag byte of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract byte getNswe(int index);
}

View File

@@ -1,130 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
public class BlockComplex extends ABlock
{
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockComplex()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates ComplexBlock.
* @param bb : Input byte buffer.
*/
public BlockComplex(ByteBuffer bb)
{
// Initialize buffer.
_buffer = new byte[GeoStructure.BLOCK_CELLS * 3];
// Load data.
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
{
// Get data.
short data = bb.getShort();
// Get nswe.
_buffer[i * 3] = (byte) (data & 0x000F);
// Get height.
data = (short) ((short) (data & 0xFFF0) >> 1);
_buffer[(i * 3) + 1] = (byte) (data & 0x00FF);
_buffer[(i * 3) + 2] = (byte) (data >> 8);
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get nswe.
return _buffer[index];
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height > worldZ ? index : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height < worldZ ? index : -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@@ -1,95 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockFlat extends ABlock
{
protected final short _height;
protected byte _nswe;
/**
* Creates FlatBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockFlat(ByteBuffer bb, GeoType type)
{
// Get height and nswe.
_height = bb.getShort();
_nswe = GeoStructure.CELL_FLAG_ALL;
// Read dummy data.
if (type == GeoType.L2OFF)
{
bb.getShort();
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height > worldZ ? 0 : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height < worldZ ? 0 : -1;
}
@Override
public short getHeight(int index)
{
return _height;
}
@Override
public byte getNswe(int index)
{
return _nswe;
}
}

View File

@@ -1,248 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockMultilayer extends ABlock
{
private static final int MAX_LAYERS = Byte.MAX_VALUE;
private static ByteBuffer _temp;
/**
* Initializes the temporary buffer.
*/
public static final void initialize()
{
// Initialize temporary buffer and sorting mechanism.
_temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3);
_temp.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Releases temporary buffer.
*/
public static final void release()
{
_temp = null;
}
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockMultilayer()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates MultilayerBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockMultilayer(ByteBuffer bb, GeoType type)
{
// Move buffer pointer to end of MultilayerBlock.
for (int cell = 0; cell < GeoStructure.BLOCK_CELLS; cell++)
{
// Get layer count for this cell.
final byte layers = type != GeoType.L2OFF ? bb.get() : (byte) bb.getShort();
if ((layers <= 0) || (layers > MAX_LAYERS))
{
throw new RuntimeException("Invalid layer count for MultilayerBlock");
}
// Add layers count.
_temp.put(layers);
// Loop over layers.
for (byte layer = 0; layer < layers; layer++)
{
// Get data.
final short data = bb.getShort();
// Add nswe and height.
_temp.put((byte) (data & 0x000F));
_temp.putShort((short) ((short) (data & 0xFFF0) >> 1));
}
}
// Initialize buffer.
_buffer = Arrays.copyOf(_temp.array(), _temp.position());
// Clear temp buffer.
_temp.clear();
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get nswe.
return _buffer[index];
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
// Loop though all cell layers, find closest layer to given worldZ.
int limit = Integer.MAX_VALUE;
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Get Z distance and compare with limit.
// Note: When 2 layers have same distance to worldZ (worldZ is in the middle of them):
// > Returns bottom layer.
// >= Returns upper layer.
final int distance = Math.abs(height - worldZ);
if (distance > limit)
{
break;
}
// Update limit and move to next layer.
limit = distance;
index += 3;
}
// Return layer index.
return index - 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// Loop though all layers, find first layer above worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is higher than worldZ, return layer index.
if (height > worldZ)
{
return index;
}
// Move index to next layer.
index -= 3;
}
// No layer found.
return -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to first layer data (first from top).
byte layers = _buffer[index++];
// Loop though all layers, find first layer below worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is lower than worldZ, return layer index.
if (height < worldZ)
{
return index;
}
// Move index to next layer.
index += 3;
}
// No layer found.
return -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public final class Cell
{
/** East NSWE flag */
public static final byte NSWE_EAST = 1 << 0;
/** West NSWE flag */
public static final byte NSWE_WEST = 1 << 1;
/** South NSWE flag */
public static final byte NSWE_SOUTH = 1 << 2;
/** North NSWE flag */
public static final byte NSWE_NORTH = 1 << 3;
/** North-East NSWE flags */
public static final byte NSWE_NORTH_EAST = NSWE_NORTH | NSWE_EAST;
/** North-West NSWE flags */
public static final byte NSWE_NORTH_WEST = NSWE_NORTH | NSWE_WEST;
/** South-East NSWE flags */
public static final byte NSWE_SOUTH_EAST = NSWE_SOUTH | NSWE_EAST;
/** South-West NSWE flags */
public static final byte NSWE_SOUTH_WEST = NSWE_SOUTH | NSWE_WEST;
/** All directions NSWE flags */
public static final byte NSWE_ALL = NSWE_EAST | NSWE_WEST | NSWE_SOUTH | NSWE_NORTH;
private Cell()
{
}
}

View File

@@ -0,0 +1,162 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.l2jmobius.gameserver.geoengine.geodata.regions.NullRegion;
import org.l2jmobius.gameserver.geoengine.geodata.regions.Region;
/**
* @author HorridoJoho
*/
public final class GeoData
{
// world dimensions: 1048576 * 1048576 = 1099511627776
private static final int WORLD_MIN_X = -655360;
private static final int WORLD_MAX_X = 393215;
private static final int WORLD_MIN_Y = -589824;
private static final int WORLD_MAX_Y = 458751;
/** Regions in the world on the x axis */
public static final int GEO_REGIONS_X = 32;
/** Regions in the world on the y axis */
public static final int GEO_REGIONS_Y = 32;
/** Region in the world */
public static final int GEO_REGIONS = GEO_REGIONS_X * GEO_REGIONS_Y;
/** Blocks in the world on the x axis */
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * IRegion.REGION_BLOCKS_X;
/** Blocks in the world on the y axis */
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * IRegion.REGION_BLOCKS_Y;
/** Blocks in the world */
public static final int GEO_BLOCKS = GEO_REGIONS * IRegion.REGION_BLOCKS;
/** Cells in the world on the x axis */
public static final int GEO_CELLS_X = GEO_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in the world in the y axis */
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** The regions array */
private final AtomicReferenceArray<IRegion> _regions = new AtomicReferenceArray<>(GEO_REGIONS);
public GeoData()
{
for (int i = 0; i < _regions.length(); i++)
{
_regions.set(i, NullRegion.INSTANCE);
}
}
private void checkGeoX(int geoX)
{
if ((geoX < 0) || (geoX >= GEO_CELLS_X))
{
throw new IllegalArgumentException();
}
}
private void checkGeoY(int geoY)
{
if ((geoY < 0) || (geoY >= GEO_CELLS_Y))
{
throw new IllegalArgumentException();
}
}
private IRegion getRegion(int geoX, int geoY)
{
checkGeoX(geoX);
checkGeoY(geoY);
return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y));
}
public void loadRegion(Path filePath, int regionX, int regionY) throws IOException
{
final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY;
try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r"))
{
_regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).order(ByteOrder.LITTLE_ENDIAN)));
}
}
public void unloadRegion(int regionX, int regionY)
{
_regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE);
}
public boolean hasGeoPos(int geoX, int geoY)
{
return getRegion(geoX, geoY).hasGeo();
}
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
public int getGeoX(int worldX)
{
if ((worldX < WORLD_MIN_X) || (worldX > WORLD_MAX_X))
{
throw new IllegalArgumentException();
}
return (worldX - WORLD_MIN_X) / 16;
}
public int getGeoY(int worldY)
{
if ((worldY < WORLD_MIN_Y) || (worldY > WORLD_MAX_Y))
{
throw new IllegalArgumentException();
}
return (worldY - WORLD_MIN_Y) / 16;
}
public int getWorldX(int geoX)
{
checkGeoX(geoX);
return (geoX * 16) + WORLD_MIN_X + 8;
}
public int getWorldY(int geoY)
{
checkGeoY(geoY);
return (geoY * 16) + WORLD_MIN_Y + 8;
}
}

View File

@@ -1,81 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import org.l2jmobius.gameserver.model.World;
public final class GeoStructure
{
// Geo cell direction (nswe) flags.
public static final byte CELL_FLAG_NONE = 0x00;
public static final byte CELL_FLAG_E = 0x01;
public static final byte CELL_FLAG_W = 0x02;
public static final byte CELL_FLAG_S = 0x04;
public static final byte CELL_FLAG_N = 0x08;
public static final byte CELL_FLAG_SE = 0x10;
public static final byte CELL_FLAG_SW = 0x20;
public static final byte CELL_FLAG_NE = 0x40;
public static final byte CELL_FLAG_NW = (byte) 0x80;
public static final byte CELL_FLAG_ALL = 0x0F;
// Geo cell expansion flags
public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW;
public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE;
public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E;
public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W;
public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E;
public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W;
// public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW;
public static final byte CELL_EXPANSION_ALL = (byte) 0xFF;
// Geo cell height constants.
public static final int CELL_SIZE = 16;
public static final int CELL_HEIGHT = 8;
public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6;
// Geo block type identification.
public static final byte TYPE_FLAT_L2J_L2OFF = 0;
public static final byte TYPE_COMPLEX_L2J = 1;
public static final byte TYPE_COMPLEX_L2OFF = 0x40;
public static final byte TYPE_MULTILAYER_L2J = 2;
// public static final byte TYPE_MULTILAYER_L2OFF = 0x41; // officially not does exist, is anything above complex block (0x41 - 0xFFFF)
// Geo block dimensions.
public static final int BLOCK_CELLS_X = 8;
public static final int BLOCK_CELLS_Y = 8;
public static final int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
// Geo region dimensions.
public static final int REGION_BLOCKS_X = 256;
public static final int REGION_BLOCKS_Y = 256;
public static final int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
public static final int REGION_CELLS_X = REGION_BLOCKS_X * BLOCK_CELLS_X;
public static final int REGION_CELLS_Y = REGION_BLOCKS_Y * BLOCK_CELLS_Y;
// Geo world dimensions.
public static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1);
public static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.TILE_Y_MIN) + 1);
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * REGION_BLOCKS_X;
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * REGION_BLOCKS_Y;
public static final int GEO_CELLS_X = GEO_BLOCKS_X * BLOCK_CELLS_X;
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * BLOCK_CELLS_Y;
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IBlock
{
int TYPE_FLAT = 0;
int TYPE_COMPLEX = 1;
int TYPE_MULTILAYER = 2;
/** Cells in a block on the x axis */
int BLOCK_CELLS_X = 8;
/** Cells in a block on the y axis */
int BLOCK_CELLS_Y = 8;
/** Cells in a block */
int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IRegion
{
/** Blocks in a region on the x axis. */
int REGION_BLOCKS_X = 256;
/** Blocks in a region on the y axis. */
int REGION_BLOCKS_Y = 256;
/** Blocks in a region. */
int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
/** Cells in a region on the x axis. */
int REGION_CELLS_X = REGION_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in a regioin on the y axis. */
int REGION_CELLS_Y = REGION_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** Cells in a region. */
int REGION_CELLS = REGION_CELLS_X * REGION_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
boolean hasGeo();
}

View File

@@ -0,0 +1,79 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public final class ComplexBlock implements IBlock
{
private final short[] _data;
public ComplexBlock(ByteBuffer bb)
{
_data = new short[IBlock.BLOCK_CELLS];
for (int cellOffset = 0; cellOffset < IBlock.BLOCK_CELLS; cellOffset++)
{
_data[cellOffset] = bb.getShort();
}
}
private short getCellData(int geoX, int geoY)
{
return _data[((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y)];
}
private byte getCellNSWE(int geoX, int geoY)
{
return (byte) (getCellData(geoX, geoY) & 0x000F);
}
private int getCellHeight(int geoX, int geoY)
{
return (short) (getCellData(geoX, geoY) & 0x0FFF0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (getCellNSWE(geoX, geoY) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getCellHeight(geoX, geoY);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = getCellHeight(geoX, geoY);
return cellHeight <= worldZ ? cellHeight : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = getCellHeight(geoX, geoY);
return cellHeight >= worldZ ? cellHeight : worldZ;
}
}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public class FlatBlock implements IBlock
{
private final short _height;
public FlatBlock(ByteBuffer bb)
{
_height = bb.getShort();
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return _height <= worldZ ? _height : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return _height >= worldZ ? _height : worldZ;
}
}

View File

@@ -0,0 +1,185 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public class MultilayerBlock implements IBlock
{
private final byte[] _data;
/**
* Initializes a new instance of this block reading the specified buffer.
* @param bb the buffer
*/
public MultilayerBlock(ByteBuffer bb)
{
final int start = bb.position();
for (int blockCellOffset = 0; blockCellOffset < IBlock.BLOCK_CELLS; blockCellOffset++)
{
final byte nLayers = bb.get();
if ((nLayers <= 0) || (nLayers > 125))
{
throw new RuntimeException("L2JGeoDriver: Geo file corrupted! Invalid layers count!");
}
bb.position(bb.position() + (nLayers * 2));
}
_data = new byte[bb.position() - start];
bb.position(start);
bb.get(_data);
}
private short getNearestLayer(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
// One layer at least was required on loading so this is set at least once on the loop below.
int nearestDZ = 0;
short nearestData = 0;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerData;
}
final int layerDZ = Math.abs(layerZ - worldZ);
if ((offset == (startOffset + 1)) || (layerDZ < nearestDZ))
{
nearestDZ = layerDZ;
nearestData = layerData;
}
}
return nearestData;
}
private int getCellDataOffset(int geoX, int geoY)
{
final int cellLocalOffset = ((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y);
int cellDataOffset = 0;
// Move index to cell, we need to parse on each request, OR we parse on creation and save indexes.
for (int i = 0; i < cellLocalOffset; i++)
{
cellDataOffset += 1 + (_data[cellDataOffset] * 2);
}
// Now the index points to the cell we need.
return cellDataOffset;
}
private short extractLayerData(int dataOffset)
{
return (short) ((_data[dataOffset] & 0xFF) | (_data[dataOffset + 1] << 8));
}
private int getNearestNSWE(int geoX, int geoY, int worldZ)
{
return extractLayerNswe(getNearestLayer(geoX, geoY, worldZ));
}
private int extractLayerNswe(short layer)
{
return (byte) (layer & 0x000F);
}
private int extractLayerHeight(short layer)
{
return (short) (layer & 0x0fff0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (getNearestNSWE(geoX, geoY, worldZ) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return extractLayerHeight(getNearestLayer(geoX, geoY, worldZ));
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int lowerZ = Integer.MIN_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerZ;
}
if ((layerZ < worldZ) && (layerZ > lowerZ))
{
lowerZ = layerZ;
}
}
return lowerZ == Integer.MIN_VALUE ? worldZ : lowerZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int higherZ = Integer.MAX_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerZ;
}
if ((layerZ > worldZ) && (layerZ < higherZ))
{
higherZ = layerZ;
}
}
return higherZ == Integer.MAX_VALUE ? worldZ : higherZ;
}
}

View File

@@ -14,55 +14,44 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.l2jmobius.gameserver.geoengine.geodata; package org.l2jmobius.gameserver.geoengine.geodata.regions;
public class BlockNull extends ABlock import org.l2jmobius.gameserver.geoengine.geodata.IRegion;
/**
* @author HorridoJoho
*/
public final class NullRegion implements IRegion
{ {
public static final NullRegion INSTANCE = new NullRegion();
@Override @Override
public boolean hasGeoPos() public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public boolean hasGeo()
{ {
return false; return false;
} }
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return GeoStructure.CELL_FLAG_ALL;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public short getHeight(int index)
{
return 0;
}
@Override
public byte getNswe(int index)
{
return GeoStructure.CELL_FLAG_ALL;
}
}

View File

@@ -0,0 +1,98 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata.regions;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
import org.l2jmobius.gameserver.geoengine.geodata.IRegion;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.ComplexBlock;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.FlatBlock;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.MultilayerBlock;
/**
* @author HorridoJoho
*/
public final class Region implements IRegion
{
private final IBlock[] _blocks = new IBlock[IRegion.REGION_BLOCKS];
public Region(ByteBuffer bb)
{
for (int blockOffset = 0; blockOffset < IRegion.REGION_BLOCKS; blockOffset++)
{
final int blockType = bb.get();
switch (blockType)
{
case IBlock.TYPE_FLAT:
{
_blocks[blockOffset] = new FlatBlock(bb);
break;
}
case IBlock.TYPE_COMPLEX:
{
_blocks[blockOffset] = new ComplexBlock(bb);
break;
}
case IBlock.TYPE_MULTILAYER:
{
_blocks[blockOffset] = new MultilayerBlock(bb);
break;
}
default:
{
throw new RuntimeException("Invalid block type " + blockType + "!");
}
}
}
}
private IBlock getBlock(int geoX, int geoY)
{
return _blocks[(((geoX / IBlock.BLOCK_CELLS_X) % IRegion.REGION_BLOCKS_X) * IRegion.REGION_BLOCKS_Y) + ((geoY / IBlock.BLOCK_CELLS_Y) % IRegion.REGION_BLOCKS_Y)];
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getBlock(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
@Override
public boolean hasGeo()
{
return true;
}
}

View File

@@ -0,0 +1,84 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
public abstract class AbstractNode<Loc extends AbstractNodeLoc>
{
private Loc _loc;
private AbstractNode<Loc> _parent;
public AbstractNode(Loc loc)
{
_loc = loc;
}
public void setParent(AbstractNode<Loc> p)
{
_parent = p;
}
public AbstractNode<Loc> getParent()
{
return _parent;
}
public Loc getLoc()
{
return _loc;
}
public void setLoc(Loc l)
{
_loc = l;
}
@Override
public int hashCode()
{
return (31 * 1) + ((_loc == null) ? 0 : _loc.hashCode());
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof AbstractNode))
{
return false;
}
final AbstractNode<?> other = (AbstractNode<?>) obj;
if (_loc == null)
{
if (other._loc != null)
{
return false;
}
}
else if (!_loc.equals(other._loc))
{
return false;
}
return true;
}
}

View File

@@ -14,22 +14,22 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.l2jmobius.gameserver.enums; package org.l2jmobius.gameserver.geoengine.pathfinding;
public enum GeoType /**
* @author -Nemesiss-
*/
public abstract class AbstractNodeLoc
{ {
L2J("%d_%d.l2j"), public abstract int getX();
L2OFF("%d_%d_conv.dat");
private final String _filename; public abstract int getY();
private GeoType(String filename) public abstract int getZ();
{
_filename = filename;
}
public String getFilename() public abstract void setZ(short z);
{
return _filename; public abstract int getNodeX();
}
} public abstract int getNodeY();
}

View File

@@ -1,119 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.model.Location;
public class Node extends Location implements Comparable<Node>
{
// Node geodata values.
private int _geoX;
private int _geoY;
private byte _nswe;
private byte _nsweExpand;
// The cost G (movement cost done) and cost H (estimated cost to target).
private int _costG;
private int _costH;
private int _costF;
// Node parent (reverse path construction).
private Node _parent;
public Node()
{
super(0, 0, 0);
}
@Override
public void clean()
{
super.clean();
_geoX = 0;
_geoY = 0;
_nswe = GeoStructure.CELL_FLAG_NONE;
_nsweExpand = GeoStructure.CELL_FLAG_NONE;
_costG = 0;
_costH = 0;
_costF = 0;
_parent = null;
}
public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand)
{
super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz);
_geoX = gx;
_geoY = gy;
_nswe = nswe;
_nsweExpand = nsweExpand;
}
public void setCost(Node parent, int weight, int costH)
{
_costG = weight;
if (parent != null)
{
_costG += parent._costG;
}
_costH = costH;
_costF = _costG + _costH;
_parent = parent;
}
public int getGeoX()
{
return _geoX;
}
public int getGeoY()
{
return _geoY;
}
public byte getNSWE()
{
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;
}
public Node getParent()
{
return _parent;
}
@Override
public int compareTo(Node o)
{
return _costF - o._costF;
}
}

View File

@@ -1,543 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.locks.ReentrantLock;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.ABlock;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.model.Location;
public class NodeBuffer
{
// Locking NodeBuffer to ensure thread-safe operations.
private final ReentrantLock _lock = new ReentrantLock();
// Container holding all available Nodes to be used.
private final Node[] _buffer;
private int _bufferIndex;
// Container (binary-heap) holding Nodes to be explored.
private final PriorityQueue<Node> _opened;
// Container holding Nodes already explored.
private final List<Node> _closed;
// Target coordinates.
private int _gtx;
private int _gty;
private int _gtz;
private Node _current;
/**
* Constructor of NodeBuffer.
* @param size : The total size buffer. Determines the amount of {@link Node}s to be used for pathfinding.
*/
public NodeBuffer(int size)
{
// Create buffers based on given size.
_buffer = new Node[size];
_opened = new PriorityQueue<>(size);
_closed = new ArrayList<>(size);
// Create Nodes.
for (int i = 0; i < size; i++)
{
_buffer[i] = new Node();
}
}
/**
* Find path consisting of Nodes. Starts at origin coordinates, ends in target coordinates.
* @param gox : origin point x
* @param goy : origin point y
* @param goz : origin point z
* @param gtx : target point x
* @param gty : target point y
* @param gtz : target point z
* @return The list of {@link Location} for the path. Empty, if path not found.
*/
public List<Location> findPath(int gox, int goy, int goz, int gtx, int gty, int gtz)
{
// Set target coordinates.
_gtx = gtx;
_gty = gty;
_gtz = gtz;
// Get node from buffer.
_current = _buffer[_bufferIndex++];
// Set node geodata coordinates and movement cost.
_current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL);
_current.setCost(null, 0, getCostH(gox, goy, goz));
int count = 0;
do
{
// Move node to closed list.
_closed.add(_current);
// Target reached, calculate path and return.
if ((_current.getGeoX() == _gtx) && (_current.getGeoY() == _gty) && (_current.getZ() == _gtz))
{
return constructPath();
}
// Expand current node.
expand();
// Get next node to expand.
_current = _opened.poll();
}
while ((_current != null) && (_bufferIndex < _buffer.length) && (++count < Config.MAX_ITERATIONS));
// Iteration failed, return empty path.
return Collections.emptyList();
}
/**
* Build the path from subsequent nodes. Skip nodes in straight directions, keep only corner nodes.
* @return List of {@link Node}s representing the path.
*/
private List<Location> constructPath()
{
// Create result.
final LinkedList<Location> path = new LinkedList<>();
// Clear X/Y direction.
int dx = 0;
int dy = 0;
// Get parent node.
Node parent = _current.getParent();
// While parent exists.
while (parent != null)
{
// Get parent node to current node X/Y direction.
final int nx = parent.getGeoX() - _current.getGeoX();
final int ny = parent.getGeoY() - _current.getGeoY();
// Direction has changed?
if ((dx != nx) || (dy != ny))
{
// Add current node to the beginning of the path (Node must be cloned, as NodeBuffer reuses them).
path.addFirst(_current.clone());
// Update X/Y direction.
dx = nx;
dy = ny;
}
// Move current node and update its parent.
_current = parent;
parent = _current.getParent();
}
return path;
}
public boolean isLocked()
{
return _lock.tryLock();
}
public void free()
{
_opened.clear();
_closed.clear();
for (int i = 0; i < (_bufferIndex - 1); i++)
{
_buffer[i].clean();
}
_bufferIndex = 0;
_current = null;
_lock.unlock();
}
/**
* Expand the current {@link Node} by exploring its neighbors (axially and diagonally).
*/
private void expand()
{
// Movement is blocked, skip.
final byte nswe = _current.getNSWE();
byte expand = _current.getNsweExpand();
if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE)
{
return;
}
// Get geo coordinates of the node to be expanded.
// Note: Z coord shifted up to avoid dual-layer issues.
final int x = _current.getGeoX();
final int y = _current.getGeoY();
final int z = _current.getZ() + GeoStructure.CELL_IGNORE_HEIGHT;
byte nsweN = GeoStructure.CELL_FLAG_NONE;
byte nsweS = GeoStructure.CELL_FLAG_NONE;
byte nsweW = GeoStructure.CELL_FLAG_NONE;
byte nsweE = GeoStructure.CELL_FLAG_NONE;
switch (expand)
{
case GeoStructure.CELL_EXPANSION_N:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_S:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_W:
{
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))
{
if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))
{
if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_E:
{
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))
{
if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))
{
if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_NW:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_NE:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_SW:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_SE:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_ALL:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
break;
}
}
}
private static byte getNodeNswe(int gx, int gy, int gz)
{
// Check new node is out of geodata grid (world coordinates).
if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y))
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get geodata block and check if there is a layer at given coordinates.
ABlock block = GeoEngine.getInstance().getBlock(gx, gy);
final int index = block.getIndexBelow(gx, gy, gz);
if (index < 0)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get node geodata nswe.
return block.getNswe(index);
}
/**
* Take {@link Node} from buffer, validate it and add to opened list.
* @param gx : The new node X geodata coordinate.
* @param gy : The new node Y geodata coordinate.
* @param gzValue : The new node Z geodata coordinate.
* @param nsweExpand : The allowed directions to be expanded on the new node.
* @param diagonal : The new node is being explored in diagonal direction.
* @return The nswe of the added node. Blank, if not added.
*/
private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal)
{
// Check new node is out of geodata grid (world coordinates).
if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y))
{
return GeoStructure.CELL_FLAG_NONE;
}
// Check buffer has reached capacity.
if (_bufferIndex >= _buffer.length)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get geodata block and check if there is a layer at given coordinates.
final ABlock block = GeoEngine.getInstance().getBlock(gx, gy);
final int index = block.getIndexBelow(gx, gy, gzValue);
if (index < 0)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get node geodata Z and nswe.
final int gz = block.getHeight(index);
final byte nswe = block.getNswe(index);
// Get node from current index (don't move index yet).
Node node = _buffer[_bufferIndex];
// Calculate node weight.
int weight;
if (nswe == GeoStructure.CELL_FLAG_ALL)
{
weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT;
}
else
{
weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT;
}
// Set node geodata coordinates.
node.setGeo(gx, gy, gz, nswe, nsweExpand);
// Node is already added to opened list, return.
if (_opened.contains(node))
{
return nswe;
}
// Node was already expanded, return.
if (_closed.contains(node))
{
return nswe;
}
// The node is to be used. Set node movement cost and add it to opened list. Move the buffer index.
node.setCost(_current, weight, getCostH(gx, gy, gz));
_opened.add(node);
_bufferIndex++;
return nswe;
}
/**
* Calculate cost H value, calculated using diagonal distance method.<br>
* Note: Manhattan distance is too simple, causing to explore more unwanted cells.
* @param gx : The node geodata X coordinate.
* @param gy : The node geodata Y coordinate.
* @param gz : The node geodata Z coordinate.
* @return The cost H value (estimated cost to reach the target).
*/
private int getCostH(int gx, int gy, int gz)
{
// Get differences to the target.
int dx = Math.abs(gx - _gtx);
int dy = Math.abs(gy - _gty);
int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT;
// Get diagonal and axial differences to the target.
final int ds = Math.min(dx, Math.min(dy, dz));
dx -= ds;
dy -= ds;
dz -= ds;
int dd;
int d;
if (dx == 0)
{
dd = Math.min(dy, dz);
d = Math.max(dy, dz) - dd;
}
else if (dy == 0)
{
dd = Math.min(dx, dz);
d = Math.max(dx, dz) - dd;
}
else
{
dd = Math.min(dx, dy);
d = Math.max(dx, dy) - dd;
}
// Calculate the diagonal distance of the node to the target.
return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2);
}
}

View File

@@ -0,0 +1,100 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.List;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes.CellPathFinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.geonodes.GeoPathFinding;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author -Nemesiss-
*/
public abstract class PathFinding
{
public static PathFinding getInstance()
{
return Config.PATHFINDING == 1 ? GeoPathFinding.getInstance() : CellPathFinding.getInstance();
}
public abstract boolean pathNodesExist(short regionoffset);
public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable);
/**
* Convert geodata position to pathnode position
* @param geoPos
* @return pathnode position
*/
public short getNodePos(int geoPos)
{
return (short) (geoPos >> 3); // OK?
}
/**
* Convert node position to pathnode block position
* @param nodePos
* @return pathnode block position (0...255)
*/
public short getNodeBlock(int nodePos)
{
return (short) (nodePos % 256);
}
public byte getRegionX(int nodePos)
{
return (byte) ((nodePos >> 8) + World.TILE_X_MIN);
}
public byte getRegionY(int nodePos)
{
return (byte) ((nodePos >> 8) + World.TILE_Y_MIN);
}
public short getRegionOffset(byte rx, byte ry)
{
return (short) ((rx << 5) + ry);
}
/**
* Convert pathnode x to World x position
* @param nodeX rx
* @return
*/
public int calculateWorldX(short nodeX)
{
return World.WORLD_X_MIN + (nodeX * 128) + 48;
}
/**
* Convert pathnode y to World y position
* @param nodeY
* @return
*/
public int calculateWorldY(short nodeY)
{
return World.WORLD_Y_MIN + (nodeY * 128) + 48;
}
public String[] getStat()
{
return null;
}
}

View File

@@ -0,0 +1,69 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
public class CellNode extends AbstractNode<NodeLoc>
{
private CellNode _next = null;
private boolean _isInUse = true;
private float _cost = -1000;
public CellNode(NodeLoc loc)
{
super(loc);
}
public boolean isInUse()
{
return _isInUse;
}
public void setInUse()
{
_isInUse = true;
}
public CellNode getNext()
{
return _next;
}
public void setNext(CellNode next)
{
_next = next;
}
public float getCost()
{
return _cost;
}
public void setCost(double cost)
{
_cost = (float) cost;
}
public void free()
{
setParent(null);
_cost = -1000;
_isInUse = false;
_next = null;
}
}

View File

@@ -0,0 +1,335 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.l2jmobius.Config;
/**
* @author DS Credits to Diamond
*/
public class CellNodeBuffer
{
private static final int MAX_ITERATIONS = 3500;
private final ReentrantLock _lock = new ReentrantLock();
private final int _mapSize;
private final CellNode[][] _buffer;
private int _baseX = 0;
private int _baseY = 0;
private int _targetX = 0;
private int _targetY = 0;
private int _targetZ = 0;
private long _timeStamp = 0;
private long _lastElapsedTime = 0;
private CellNode _current = null;
public CellNodeBuffer(int size)
{
_mapSize = size;
_buffer = new CellNode[_mapSize][_mapSize];
}
public final boolean lock()
{
return _lock.tryLock();
}
public final CellNode findPath(int x, int y, int z, int tx, int ty, int tz)
{
_timeStamp = System.currentTimeMillis();
_baseX = x + ((tx - x - _mapSize) / 2); // Middle of the line (x,y) - (tx,ty).
_baseY = y + ((ty - y - _mapSize) / 2); // Will be in the center of the buffer.
_targetX = tx;
_targetY = ty;
_targetZ = tz;
_current = getNode(x, y, z);
_current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
for (int count = 0; count < MAX_ITERATIONS; count++)
{
if ((_current.getLoc().getNodeX() == _targetX) && (_current.getLoc().getNodeY() == _targetY) && (Math.abs(_current.getLoc().getZ() - _targetZ) < 64))
{
return _current; // Found.
}
getNeighbors();
if (_current.getNext() == null)
{
return null; // No more ways.
}
_current = _current.getNext();
}
return null;
}
public final void free()
{
_current = null;
CellNode node;
for (int i = 0; i < _mapSize; i++)
{
for (int j = 0; j < _mapSize; j++)
{
node = _buffer[i][j];
if (node != null)
{
node.free();
}
}
}
_lock.unlock();
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
}
public final long getElapsedTime()
{
return _lastElapsedTime;
}
public final List<CellNode> debugPath()
{
final List<CellNode> result = new LinkedList<>();
for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
{
result.add(n);
n.setCost(-n.getCost());
}
for (int i = 0; i < _mapSize; i++)
{
for (int j = 0; j < _mapSize; j++)
{
final CellNode n = _buffer[i][j];
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
{
continue;
}
result.add(n);
}
}
return result;
}
private final void getNeighbors()
{
if (_current.getLoc().canGoNone())
{
return;
}
final int x = _current.getLoc().getNodeX();
final int y = _current.getLoc().getNodeY();
final int z = _current.getLoc().getZ();
CellNode nodeE = null;
CellNode nodeS = null;
CellNode nodeW = null;
CellNode nodeN = null;
// East
if (_current.getLoc().canGoEast())
{
nodeE = addNode(x + 1, y, z, false);
}
// South
if (_current.getLoc().canGoSouth())
{
nodeS = addNode(x, y + 1, z, false);
}
// West
if (_current.getLoc().canGoWest())
{
nodeW = addNode(x - 1, y, z, false);
}
// North
if (_current.getLoc().canGoNorth())
{
nodeN = addNode(x, y - 1, z, false);
}
if (!Config.ADVANCED_DIAGONAL_STRATEGY)
{
return;
}
// SouthEast
if ((nodeE != null) && (nodeS != null) && nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast())
{
addNode(x + 1, y + 1, z, true);
}
// SouthWest
if ((nodeS != null) && (nodeW != null) && nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest())
{
addNode(x - 1, y + 1, z, true);
}
// NorthEast
if ((nodeN != null) && (nodeE != null) && nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast())
{
addNode(x + 1, y - 1, z, true);
}
// NorthWest
if ((nodeN != null) && (nodeW != null) && nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest())
{
addNode(x - 1, y - 1, z, true);
}
}
private final CellNode getNode(int x, int y, int z)
{
final int aX = x - _baseX;
if ((aX < 0) || (aX >= _mapSize))
{
return null;
}
final int aY = y - _baseY;
if ((aY < 0) || (aY >= _mapSize))
{
return null;
}
CellNode result = _buffer[aX][aY];
if (result == null)
{
result = new CellNode(new NodeLoc(x, y, z));
_buffer[aX][aY] = result;
}
else if (!result.isInUse())
{
result.setInUse();
// Re-init node if needed.
if (result.getLoc() != null)
{
result.getLoc().set(x, y, z);
}
else
{
result.setLoc(new NodeLoc(x, y, z));
}
}
return result;
}
private final CellNode addNode(int x, int y, int z, boolean diagonal)
{
final CellNode newNode = getNode(x, y, z);
if (newNode == null)
{
return null;
}
if (newNode.getCost() >= 0)
{
return newNode;
}
final int geoZ = newNode.getLoc().getZ();
final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
if (!newNode.getLoc().canGoAll() || (stepZ > 16))
{
weight = Config.HIGH_WEIGHT;
}
else if (isHighWeight(x + 1, y, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x - 1, y, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x, y + 1, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
else if (isHighWeight(x, y - 1, geoZ))
{
weight = Config.MEDIUM_WEIGHT;
}
newNode.setParent(_current);
newNode.setCost(getCost(x, y, geoZ, weight));
CellNode node = _current;
int count = 0;
while ((node.getNext() != null) && (count < (MAX_ITERATIONS * 4)))
{
count++;
if (node.getNext().getCost() > newNode.getCost())
{
// Insert node into a chain.
newNode.setNext(node.getNext());
break;
}
node = node.getNext();
}
if (count == (MAX_ITERATIONS * 4))
{
System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
}
node.setNext(newNode); // Add last.
return newNode;
}
private final boolean isHighWeight(int x, int y, int z)
{
final CellNode result = getNode(x, y, z);
return (result == null) || !result.getLoc().canGoAll() || (Math.abs(result.getLoc().getZ() - z) > 16);
}
private final double getCost(int x, int y, int z, float weight)
{
final int dX = x - _targetX;
final int dY = y - _targetY;
final int dZ = z - _targetZ;
// Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16
double result = Math.sqrt((dX * dX) + (dY * dY) + ((dZ * dZ) / 256.0));
if (result > weight)
{
result += weight;
}
if (result > Float.MAX_VALUE)
{
result = Float.MAX_VALUE;
}
return result;
}
}

View File

@@ -0,0 +1,403 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.instancemanager.IdManager;
import org.l2jmobius.gameserver.model.instancezone.Instance;
import org.l2jmobius.gameserver.model.item.instance.Item;
/**
* @author Sami, DS Credits to Diamond
*/
public class CellPathFinding extends PathFinding
{
private static final Logger LOGGER = Logger.getLogger(CellPathFinding.class.getName());
private BufferInfo[] _allBuffers;
private int _findSuccess = 0;
private int _findFails = 0;
private int _postFilterUses = 0;
private int _postFilterPlayableUses = 0;
private int _postFilterPasses = 0;
private long _postFilterElapsed = 0;
private List<Item> _debugItems = null;
protected CellPathFinding()
{
try
{
final String[] array = Config.PATHFIND_BUFFERS.split(";");
_allBuffers = new BufferInfo[array.length];
String buf;
String[] args;
for (int i = 0; i < array.length; i++)
{
buf = array[i];
args = buf.split("x");
if (args.length != 2)
{
throw new Exception("Invalid buffer definition: " + buf);
}
_allBuffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e);
throw new Error("CellPathFinding: load aborted");
}
}
@Override
public boolean pathNodesExist(short regionoffset)
{
return false;
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
{
final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getInstance().getGeoY(y);
if (!GeoEngine.getInstance().hasGeo(x, y))
{
return null;
}
final int gz = GeoEngine.getInstance().getHeight(x, y, z);
final int gtx = GeoEngine.getInstance().getGeoX(tx);
final int gty = GeoEngine.getInstance().getGeoY(ty);
if (!GeoEngine.getInstance().hasGeo(tx, ty))
{
return null;
}
final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz);
final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty))), playable);
if (buffer == null)
{
return null;
}
final boolean debug = Config.DEBUG_PATH && playable;
if (debug)
{
if (_debugItems == null)
{
_debugItems = new CopyOnWriteArrayList<>();
}
else
{
for (Item item : _debugItems)
{
item.decayMe();
}
_debugItems.clear();
}
}
List<AbstractNodeLoc> path = null;
try
{
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
if (debug)
{
for (CellNode n : buffer.debugPath())
{
if (n.getCost() < 0)
{
dropDebugItem(1831, (int) (-n.getCost() * 10), n.getLoc());
}
else
{
// Known nodes.
dropDebugItem(57, (int) (n.getCost() * 10), n.getLoc());
}
}
}
if (result == null)
{
_findFails++;
return null;
}
path = constructPath(result);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
return null;
}
finally
{
buffer.free();
}
if ((path.size() < 3) || (Config.MAX_POSTFILTER_PASSES <= 0))
{
_findSuccess++;
return path;
}
final long timeStamp = System.currentTimeMillis();
_postFilterUses++;
if (playable)
{
_postFilterPlayableUses++;
}
boolean remove;
int pass = 0;
do
{
pass++;
_postFilterPasses++;
remove = false;
final Iterator<AbstractNodeLoc> endPoint = path.iterator();
endPoint.next();
int currentX = x;
int currentY = y;
int currentZ = z;
int midPoint = 0;
while (endPoint.hasNext())
{
final AbstractNodeLoc locMiddle = path.get(midPoint);
final AbstractNodeLoc locEnd = endPoint.next();
if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance))
{
path.remove(midPoint);
remove = true;
if (debug)
{
dropDebugItem(735, 1, locMiddle);
}
}
else
{
currentX = locMiddle.getX();
currentY = locMiddle.getY();
currentZ = locMiddle.getZ();
midPoint++;
}
}
}
// Only one postfilter pass for AI.
while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES));
if (debug)
{
path.forEach(n -> dropDebugItem(65, 1, n));
}
_findSuccess++;
_postFilterElapsed += System.currentTimeMillis() - timeStamp;
return path;
}
private List<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> node)
{
final List<AbstractNodeLoc> path = new CopyOnWriteArrayList<>();
int previousDirectionX = Integer.MIN_VALUE;
int previousDirectionY = Integer.MIN_VALUE;
int directionX;
int directionY;
AbstractNode<NodeLoc> tempNode = node;
while (tempNode.getParent() != null)
{
if (!Config.ADVANCED_DIAGONAL_STRATEGY && (tempNode.getParent().getParent() != null))
{
final int tmpX = tempNode.getLoc().getNodeX() - tempNode.getParent().getParent().getLoc().getNodeX();
final int tmpY = tempNode.getLoc().getNodeY() - tempNode.getParent().getParent().getLoc().getNodeY();
if (Math.abs(tmpX) == Math.abs(tmpY))
{
directionX = tmpX;
directionY = tmpY;
}
else
{
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
}
}
else
{
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
}
// Only add a new route point if moving direction changes.
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.add(0, tempNode.getLoc());
tempNode.setLoc(null);
}
tempNode = tempNode.getParent();
}
return path;
}
private final CellNodeBuffer alloc(int size, boolean playable)
{
CellNodeBuffer current = null;
for (BufferInfo i : _allBuffers)
{
if (i.mapSize >= size)
{
for (CellNodeBuffer buf : i.bufs)
{
if (buf.lock())
{
i.uses++;
if (playable)
{
i.playableUses++;
}
i.elapsed += buf.getElapsedTime();
current = buf;
break;
}
}
if (current != null)
{
break;
}
// Not found, allocate temporary buffer.
current = new CellNodeBuffer(i.mapSize);
current.lock();
if (i.bufs.size() < i.count)
{
i.bufs.add(current);
i.uses++;
if (playable)
{
i.playableUses++;
}
break;
}
i.overflows++;
if (playable)
{
i.playableOverflows++;
// System.err.println("Overflow, size requested: " + size + " playable:"+playable);
}
}
}
return current;
}
private final void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
{
final Item item = new Item(IdManager.getInstance().getNextId(), itemId);
item.setCount(num);
item.spawnMe(loc.getX(), loc.getY(), loc.getZ());
_debugItems.add(item);
}
private static final class BufferInfo
{
final int mapSize;
final int count;
List<CellNodeBuffer> bufs;
int uses = 0;
int playableUses = 0;
int overflows = 0;
int playableOverflows = 0;
long elapsed = 0;
public BufferInfo(int size, int cnt)
{
mapSize = size;
count = cnt;
bufs = new ArrayList<>(count);
}
@Override
public String toString()
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat, String.valueOf(mapSize), "x", String.valueOf(mapSize), " num:", String.valueOf(bufs.size()), "/", String.valueOf(count), " uses:", String.valueOf(uses), "/", String.valueOf(playableUses));
if (uses > 0)
{
StringUtil.append(stat, " total/avg(ms):", String.valueOf(elapsed), "/", String.format("%1.2f", (double) elapsed / uses));
}
StringUtil.append(stat, " ovf:", String.valueOf(overflows), "/", String.valueOf(playableOverflows));
return stat.toString();
}
}
@Override
public String[] getStat()
{
final String[] result = new String[_allBuffers.length + 1];
for (int i = 0; i < _allBuffers.length; i++)
{
result[i] = _allBuffers[i].toString();
}
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat, "LOS postfilter uses:", String.valueOf(_postFilterUses), "/", String.valueOf(_postFilterPlayableUses));
if (_postFilterUses > 0)
{
StringUtil.append(stat, " total/avg(ms):", String.valueOf(_postFilterElapsed), "/", String.format("%1.2f", (double) _postFilterElapsed / _postFilterUses), " passes total/avg:", String.valueOf(_postFilterPasses), "/", String.format("%1.1f", (double) _postFilterPasses / _postFilterUses), Config.EOL);
}
StringUtil.append(stat, "Pathfind success/fail:", String.valueOf(_findSuccess), "/", String.valueOf(_findFails));
result[result.length - 1] = stat.toString();
return result;
}
public static CellPathFinding getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final CellPathFinding INSTANCE = new CellPathFinding();
}
}

View File

@@ -0,0 +1,165 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.Cell;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
/**
* @author -Nemesiss-, HorridoJoho
*/
public class NodeLoc extends AbstractNodeLoc
{
private int _x;
private int _y;
private boolean _goNorth;
private boolean _goEast;
private boolean _goSouth;
private boolean _goWest;
private int _geoHeight;
public NodeLoc(int x, int y, int z)
{
set(x, y, z);
}
public void set(int x, int y, int z)
{
_x = x;
_y = y;
_goNorth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH);
_goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST);
_goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH);
_goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST);
_geoHeight = GeoEngine.getInstance().getNearestZ(x, y, z);
}
public boolean canGoNorth()
{
return _goNorth;
}
public boolean canGoEast()
{
return _goEast;
}
public boolean canGoSouth()
{
return _goSouth;
}
public boolean canGoWest()
{
return _goWest;
}
public boolean canGoNone()
{
return !canGoNorth() && !canGoEast() && !canGoSouth() && !canGoWest();
}
public boolean canGoAll()
{
return canGoNorth() && canGoEast() && canGoSouth() && canGoWest();
}
@Override
public int getX()
{
return GeoEngine.getInstance().getWorldX(_x);
}
@Override
public int getY()
{
return GeoEngine.getInstance().getWorldY(_y);
}
@Override
public int getZ()
{
return _geoHeight;
}
@Override
public void setZ(short z)
{
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
int nswe = 0;
if (canGoNorth())
{
nswe |= Cell.NSWE_NORTH;
}
if (canGoEast())
{
nswe |= Cell.NSWE_EAST;
}
if (canGoSouth())
{
nswe |= Cell.NSWE_SOUTH;
}
if (canGoWest())
{
nswe |= Cell.NSWE_WEST;
}
result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe);
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof NodeLoc))
{
return false;
}
final NodeLoc other = (NodeLoc) obj;
return (_x == other._x) && (_y == other._y) && (!_goNorth == !other._goNorth) && (!_goEast == !other._goEast) && (!_goSouth == !other._goSouth) && (!_goWest == !other._goWest) && (_geoHeight == other._geoHeight);
}
}

View File

@@ -0,0 +1,60 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class GeoNode extends AbstractNode<GeoNodeLoc>
{
private final int _neighborsIdx;
private short _cost;
private GeoNode[] _neighbors;
public GeoNode(GeoNodeLoc loc, int neighborsIdx)
{
super(loc);
_neighborsIdx = neighborsIdx;
}
public short getCost()
{
return _cost;
}
public void setCost(int cost)
{
_cost = (short) cost;
}
public GeoNode[] getNeighbors()
{
return _neighbors;
}
public void attachNeighbors(GeoNode[] neighbors)
{
_neighbors = neighbors;
}
public int getNeighborsIdx()
{
return _neighborsIdx;
}
}

View File

@@ -0,0 +1,102 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.model.World;
/**
* @author -Nemesiss-
*/
public class GeoNodeLoc extends AbstractNodeLoc
{
private final short _x;
private final short _y;
private final short _z;
public GeoNodeLoc(short x, short y, short z)
{
_x = x;
_y = y;
_z = z;
}
@Override
public int getX()
{
return World.WORLD_X_MIN + (_x * 128) + 48;
}
@Override
public int getY()
{
return World.WORLD_Y_MIN + (_y * 128) + 48;
}
@Override
public int getZ()
{
return _z;
}
@Override
public void setZ(short z)
{
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
result = (prime * result) + _z;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof GeoNodeLoc))
{
return false;
}
final GeoNodeLoc other = (GeoNodeLoc) obj;
return (_x == other._x) && (_y == other._y) && (_z == other._z);
}
}

View File

@@ -0,0 +1,445 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.utils.FastNodeList;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author -Nemesiss-
*/
public class GeoPathFinding extends PathFinding
{
private static final Logger LOGGER = Logger.getLogger(GeoPathFinding.class.getName());
private static final Map<Short, ByteBuffer> PATH_NODES = new HashMap<>();
private static final Map<Short, IntBuffer> PATH_NODE_INDEX = new HashMap<>();
@Override
public boolean pathNodesExist(short regionoffset)
{
return PATH_NODE_INDEX.containsKey(regionoffset);
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
{
final int gx = (x - World.WORLD_X_MIN) >> 4;
final int gy = (y - World.WORLD_Y_MIN) >> 4;
final short gz = (short) z;
final int gtx = (tx - World.WORLD_X_MIN) >> 4;
final int gty = (ty - World.WORLD_Y_MIN) >> 4;
final short gtz = (short) tz;
final GeoNode start = readNode(gx, gy, gz);
final GeoNode end = readNode(gtx, gty, gtz);
if ((start == null) || (end == null))
{
return null;
}
if (Math.abs(start.getLoc().getZ() - z) > 55)
{
return null; // Not correct layer.
}
if (Math.abs(end.getLoc().getZ() - tz) > 55)
{
return null; // Not correct layer.
}
if (start == end)
{
return null;
}
// TODO: Find closest path node we CAN access. Now only checks if we can not reach the closest.
Location temp = GeoEngine.getInstance().getValidLocation(x, y, z, start.getLoc().getX(), start.getLoc().getY(), start.getLoc().getZ(), instance);
if ((temp.getX() != start.getLoc().getX()) || (temp.getY() != start.getLoc().getY()))
{
return null; // Cannot reach closest...
}
// TODO: Find closest path node around target, now only checks if final location can be reached.
temp = GeoEngine.getInstance().getValidLocation(tx, ty, tz, end.getLoc().getX(), end.getLoc().getY(), end.getLoc().getZ(), instance);
if ((temp.getX() != end.getLoc().getX()) || (temp.getY() != end.getLoc().getY()))
{
return null; // Cannot reach closest...
}
return searchByClosest2(start, end);
}
public List<AbstractNodeLoc> searchByClosest2(GeoNode start, GeoNode end)
{
// Always continues checking from the closest to target non-blocked
// node from to_visit list. There's extra length in path if needed
// to go backwards/sideways but when moving generally forwards, this is extra fast
// and accurate. And can reach insane distances (try it with 800 nodes..).
// Minimum required node count would be around 300-400.
// Generally returns a bit (only a bit) more intelligent looking routes than
// the basic version. Not a true distance image (which would increase CPU
// load) level of intelligence though.
// List of Visited Nodes.
final FastNodeList visited = new FastNodeList(550);
// List of Nodes to Visit.
final LinkedList<GeoNode> toVisit = new LinkedList<>();
toVisit.add(start);
final int targetX = end.getLoc().getNodeX();
final int targetY = end.getLoc().getNodeY();
int dx, dy;
boolean added;
int i = 0;
while (i < 550)
{
GeoNode node;
try
{
node = toVisit.removeFirst();
}
catch (Exception e)
{
// No Path found
return null;
}
if (node.equals(end))
{
return constructPath2(node);
}
i++;
visited.add(node);
node.attachNeighbors(readNeighbors(node));
final GeoNode[] neighbors = node.getNeighbors();
if (neighbors == null)
{
continue;
}
for (GeoNode n : neighbors)
{
if (!visited.containsRev(n) && !toVisit.contains(n))
{
added = false;
n.setParent(node);
dx = targetX - n.getLoc().getNodeX();
dy = targetY - n.getLoc().getNodeY();
n.setCost((dx * dx) + (dy * dy));
for (int index = 0; index < toVisit.size(); index++)
{
// Supposed to find it quite early.
if (toVisit.get(index).getCost() > n.getCost())
{
toVisit.add(index, n);
added = true;
break;
}
}
if (!added)
{
toVisit.addLast(n);
}
}
}
}
// No Path found.
return null;
}
public List<AbstractNodeLoc> constructPath2(AbstractNode<GeoNodeLoc> node)
{
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
int previousDirectionX = -1000;
int previousDirectionY = -1000;
int directionX;
int directionY;
AbstractNode<GeoNodeLoc> tempNode = node;
while (tempNode.getParent() != null)
{
// Only add a new route point if moving direction changes.
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.addFirst(tempNode.getLoc());
}
tempNode = tempNode.getParent();
}
return path;
}
private GeoNode[] readNeighbors(GeoNode n)
{
if (n.getLoc() == null)
{
return null;
}
int idx = n.getNeighborsIdx();
final int nodeX = n.getLoc().getNodeX();
final int nodeY = n.getLoc().getNodeY();
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
final ByteBuffer pn = PATH_NODES.get(regoffset);
final List<AbstractNode<GeoNodeLoc>> neighbors = new ArrayList<>(8);
GeoNode newNode;
short newNodeX;
short newNodeY;
// Region for sure will change, we must read from correct file
byte neighbor = pn.get(idx++); // N
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) nodeX;
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NE
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // E
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) nodeY;
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SE
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // S
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) nodeX;
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SW
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // W
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) nodeY;
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NW
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
return neighbors.toArray(new GeoNode[neighbors.size()]);
}
// Private
private GeoNode readNode(short nodeX, short nodeY, byte layer)
{
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(nodeX);
final short nby = getNodeBlock(nodeY);
int idx = PATH_NODE_INDEX.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = PATH_NODES.get(regoffset);
// Reading.
final byte nodes = pn.get(idx);
idx += (layer * 10) + 1; // byte + layer*10byte
if (nodes < layer)
{
LOGGER.warning("SmthWrong!");
}
final short node_z = pn.getShort(idx);
idx += 2;
return new GeoNode(new GeoNodeLoc(nodeX, nodeY, node_z), idx);
}
private GeoNode readNode(int gx, int gy, short z)
{
final short nodeX = getNodePos(gx);
final short nodeY = getNodePos(gy);
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(nodeX);
final short nby = getNodeBlock(nodeY);
int idx = PATH_NODE_INDEX.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = PATH_NODES.get(regoffset);
// Reading.
byte nodes = pn.get(idx++);
int idx2 = 0; // Create index to nearlest node by z.
short lastZ = Short.MIN_VALUE;
while (nodes > 0)
{
final short node_z = pn.getShort(idx);
if (Math.abs(lastZ - z) > Math.abs(node_z - z))
{
lastZ = node_z;
idx2 = idx + 2;
}
idx += 10; // short + 8 byte
nodes--;
}
return new GeoNode(new GeoNodeLoc(nodeX, nodeY, lastZ), idx2);
}
protected GeoPathFinding()
{
for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++)
{
for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++)
{
loadPathNodeFile((byte) regionX, (byte) regionY);
}
}
}
private void loadPathNodeFile(byte rx, byte ry)
{
final short regionoffset = getRegionOffset(rx, ry);
final File file = new File(Config.PATHNODE_PATH.toString(), rx + "_" + ry + ".pn");
if (!file.exists())
{
return;
}
// LOGGER.info("Path Engine: - Loading: " + file.getName() + " -> region offset: " + regionoffset + " X: " + rx + " Y: " + ry);
int node = 0;
int size = 0;
int index = 0;
// Create a read-only memory-mapped file.
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel roChannel = raf.getChannel())
{
size = (int) roChannel.size();
final MappedByteBuffer nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
// Indexing pathnode files, so we will know where each block starts.
final IntBuffer indexs = IntBuffer.allocate(65536);
while (node < 65536)
{
final byte layer = nodes.get(index);
indexs.put(node++, index);
index += (layer * 10) + 1;
}
PATH_NODE_INDEX.put(regionoffset, indexs);
PATH_NODES.put(regionoffset, nodes);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to Load PathNode File: " + file.getAbsolutePath() + " : " + e.getMessage(), e);
}
}
public static GeoPathFinding getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final GeoPathFinding INSTANCE = new GeoPathFinding();
}
}

View File

@@ -0,0 +1,124 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.utils;
import org.l2jmobius.gameserver.geoengine.pathfinding.geonodes.GeoNode;
/**
* @author -Nemesiss-
*/
public class BinaryNodeHeap
{
private final GeoNode[] _list;
private int _size;
public BinaryNodeHeap(int size)
{
_list = new GeoNode[size + 1];
_size = 0;
}
public void add(GeoNode n)
{
_size++;
int pos = _size;
_list[pos] = n;
while (pos != 1)
{
final int p2 = pos / 2;
if (_list[pos].getCost() <= _list[p2].getCost())
{
final GeoNode temp = _list[p2];
_list[p2] = _list[pos];
_list[pos] = temp;
pos = p2;
}
else
{
break;
}
}
}
public GeoNode removeFirst()
{
final GeoNode first = _list[1];
_list[1] = _list[_size];
_list[_size] = null;
_size--;
int pos = 1;
int cpos;
int dblcpos;
GeoNode temp;
while (true)
{
cpos = pos;
dblcpos = cpos * 2;
if ((dblcpos + 1) <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
if (_list[pos].getCost() >= _list[dblcpos + 1].getCost())
{
pos = dblcpos + 1;
}
}
else if (dblcpos <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
}
if (cpos != pos)
{
temp = _list[cpos];
_list[cpos] = _list[pos];
_list[pos] = temp;
}
else
{
break;
}
}
return first;
}
public boolean contains(GeoNode n)
{
if (_size == 0)
{
return false;
}
for (int i = 1; i <= _size; i++)
{
if (_list[i].equals(n))
{
return true;
}
}
return false;
}
public boolean isEmpty()
{
return _size == 0;
}
}

View File

@@ -0,0 +1,49 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.pathfinding.utils;
import java.util.ArrayList;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class FastNodeList
{
private final ArrayList<AbstractNode<?>> _list;
public FastNodeList(int size)
{
_list = new ArrayList<>(size);
}
public void add(AbstractNode<?> n)
{
_list.add(n);
}
public boolean contains(AbstractNode<?> n)
{
return _list.contains(n);
}
public boolean containsRev(AbstractNode<?> n)
{
return _list.lastIndexOf(n) != -1;
}
}

View File

@@ -400,7 +400,7 @@ public class Spawn extends Location implements IIdentifiable, INamable
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE); final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE); final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
if (GeoEngine.getInstance().canMoveToTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()) // if (GeoEngine.getInstance().canMoveToTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()) //
&& GeoEngine.getInstance().canSee(newlocx, newlocy, newlocz, npc.getCollisionHeight(), randX, randY, newlocz, 0)) && GeoEngine.getInstance().canSeeTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()))
{ {
newlocx = randX; newlocx = randX;
newlocy = randY; newlocy = randY;

View File

@@ -67,6 +67,8 @@ import org.l2jmobius.gameserver.enums.Team;
import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.enums.TeleportWhereType;
import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.enums.UserInfoType;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.IdManager;
import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager;
import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.QuestManager;
@@ -821,7 +823,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
{ {
int x = xValue; int x = xValue;
int y = yValue; int y = yValue;
int z = zValue; int z = _isFlying ? zValue : GeoEngine.getInstance().getHeight(x, y, zValue);
int heading = headingValue; int heading = headingValue;
Instance instance = instanceValue; Instance instance = instanceValue;
@@ -2688,7 +2690,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
public boolean disregardingGeodata; public boolean disregardingGeodata;
public int onGeodataPathIndex; public int onGeodataPathIndex;
public List<Location> geoPath; public List<AbstractNodeLoc> geoPath;
public int geoPathAccurateTx; public int geoPathAccurateTx;
public int geoPathAccurateTy; public int geoPathAccurateTy;
public int geoPathGtx; public int geoPathGtx;
@@ -3465,7 +3467,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
} }
// Movement checks. // Movement checks.
if (Config.PATHFINDING && !(this instanceof FriendlyNpc)) if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
{ {
final double originalDistance = distance; final double originalDistance = distance;
final int originalX = x; final int originalX = x;
@@ -3509,7 +3511,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle)
{ {
// Path calculation -- overrides previous movement check // Path calculation -- overrides previous movement check
m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
{ {
if (isPlayer() && !_isFlying && !isInWater) if (isPlayer() && !_isFlying && !isInWater)
@@ -3553,7 +3555,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
} }
// If no distance to go through, the movement is canceled // If no distance to go through, the movement is canceled
if ((distance < 1) && (Config.PATHFINDING || isPlayable())) if ((distance < 1) && ((Config.PATHFINDING > 0) || isPlayable()))
{ {
if (isSummon()) if (isSummon())
{ {

View File

@@ -19,7 +19,7 @@ package org.l2jmobius.gameserver.util;
import java.awt.Color; import java.awt.Color;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.geoengine.geodata.Cell;
import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive;
@@ -30,21 +30,21 @@ public final class GeoUtils
{ {
public static void debug2DLine(Player player, int x, int y, int tx, int ty, int z) public static void debug2DLine(Player player, int x, int y, int tx, int ty, int z)
{ {
final int gx = GeoEngine.getGeoX(x); final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getGeoY(y); final int gy = GeoEngine.getInstance().getGeoY(y);
final int tgx = GeoEngine.getGeoX(tx); final int tgx = GeoEngine.getInstance().getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty); final int tgy = GeoEngine.getInstance().getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z);
final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy);
while (iter.next()) while (iter.next())
{ {
final int wx = GeoEngine.getWorldX(iter.x()); final int wx = GeoEngine.getInstance().getWorldX(iter.x());
final int wy = GeoEngine.getWorldY(iter.y()); final int wy = GeoEngine.getInstance().getWorldY(iter.y());
prim.addPoint(Color.RED, wx, wy, z); prim.addPoint(Color.RED, wx, wy, z);
} }
@@ -53,21 +53,21 @@ public final class GeoUtils
public static void debug3DLine(Player player, int x, int y, int z, int tx, int ty, int tz) public static void debug3DLine(Player player, int x, int y, int z, int tx, int ty, int tz)
{ {
final int gx = GeoEngine.getGeoX(x); final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getGeoY(y); final int gy = GeoEngine.getInstance().getGeoY(y);
final int tgx = GeoEngine.getGeoX(tx); final int tgx = GeoEngine.getInstance().getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty); final int tgy = GeoEngine.getInstance().getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz); prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz);
final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz); final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz);
iter.next(); iter.next();
int prevX = iter.x(); int prevX = iter.x();
int prevY = iter.y(); int prevY = iter.y();
int wx = GeoEngine.getWorldX(prevX); int wx = GeoEngine.getInstance().getWorldX(prevX);
int wy = GeoEngine.getWorldY(prevY); int wy = GeoEngine.getInstance().getWorldY(prevY);
int wz = iter.z(); int wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz); prim.addPoint(Color.RED, wx, wy, wz);
@@ -78,8 +78,8 @@ public final class GeoUtils
if ((curX != prevX) || (curY != prevY)) if ((curX != prevX) || (curY != prevY))
{ {
wx = GeoEngine.getWorldX(curX); wx = GeoEngine.getInstance().getWorldX(curX);
wy = GeoEngine.getWorldY(curY); wy = GeoEngine.getInstance().getWorldY(curY);
wz = iter.z(); wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz); prim.addPoint(Color.RED, wx, wy, wz);
@@ -93,7 +93,7 @@ public final class GeoUtils
private static Color getDirectionColor(int x, int y, int z, int nswe) private static Color getDirectionColor(int x, int y, int z, int nswe)
{ {
if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe))
{ {
return Color.GREEN; return Color.GREEN;
} }
@@ -109,8 +109,9 @@ public final class GeoUtils
int iPacket = 0; int iPacket = 0;
ExServerPrimitive exsp = null; ExServerPrimitive exsp = null;
final int playerGx = GeoEngine.getGeoX(player.getX()); final GeoEngine ge = GeoEngine.getInstance();
final int playerGy = GeoEngine.getGeoY(player.getY()); final int playerGx = ge.getGeoX(player.getX());
final int playerGy = ge.getGeoY(player.getY());
for (int dx = -geoRadius; dx <= geoRadius; ++dx) for (int dx = -geoRadius; dx <= geoRadius; ++dx)
{ {
for (int dy = -geoRadius; dy <= geoRadius; ++dy) for (int dy = -geoRadius; dy <= geoRadius; ++dy)
@@ -134,32 +135,32 @@ public final class GeoUtils
final int gx = playerGx + dx; final int gx = playerGx + dx;
final int gy = playerGy + dy; final int gy = playerGy + dy;
final int x = GeoEngine.getWorldX(gx); final int x = ge.getWorldX(gx);
final int y = GeoEngine.getWorldY(gy); final int y = ge.getWorldY(gy);
final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); final int z = ge.getNearestZ(gx, gy, player.getZ());
// north arrow // north arrow
Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH);
exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z); exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z);
exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z); exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z);
exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z); exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z);
exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z);
// east arrow // east arrow
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST);
exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z); exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z);
exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z); exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z);
exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z); exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z);
exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z);
// south arrow // south arrow
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH);
exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z); exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z);
exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z); exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z);
exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z); exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z);
exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z); exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z);
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST);
exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z); exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z);
exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z); exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z);
exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z); exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z);
@@ -187,41 +188,41 @@ public final class GeoUtils
{ {
if (y > lastY) if (y > lastY)
{ {
return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; return Cell.NSWE_SOUTH_EAST;
} }
else if (y < lastY) else if (y < lastY)
{ {
return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; return Cell.NSWE_NORTH_EAST;
} }
else else
{ {
return GeoStructure.CELL_FLAG_E; return Cell.NSWE_EAST;
} }
} }
else if (x < lastX) // west else if (x < lastX) // west
{ {
if (y > lastY) if (y > lastY)
{ {
return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; return Cell.NSWE_SOUTH_WEST;
} }
else if (y < lastY) else if (y < lastY)
{ {
return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; return Cell.NSWE_NORTH_WEST;
} }
else else
{ {
return GeoStructure.CELL_FLAG_W; return Cell.NSWE_WEST;
} }
} }
else // unchanged x else // unchanged x
{ {
if (y > lastY) if (y > lastY)
{ {
return GeoStructure.CELL_FLAG_S; return Cell.NSWE_SOUTH;
} }
else if (y < lastY) else if (y < lastY)
{ {
return GeoStructure.CELL_FLAG_N; return Cell.NSWE_NORTH;
} }
else else
{ {

View File

@@ -298,7 +298,6 @@ CorrectPrices = True
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Allow characters to receive damage from falling. # Allow characters to receive damage from falling.
# CoordSynchronize = 2 is recommended.
# Default: True # Default: True
EnableFallingDamage = True EnableFallingDamage = True

View File

@@ -2,48 +2,47 @@
# Geodata # Geodata
# ================================================================= # =================================================================
# Specifies the path to geodata files. For example, when using geodata files located # Pathfinding options:
# at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ # 0 = Disabled
# 1 = Enabled using path node files.
# 2 = Enabled using geodata cells at runtime.
# Default: 0
PathFinding = 2
# Geodata file directory.
GeoDataPath = ./data/geodata/ GeoDataPath = ./data/geodata/
# Specifies the geodata files type. Default: L2J # Pathnode file directory.
# L2J: Using L2J geodata files (filename e.g. 22_16.l2j) # Default: pathnode
# L2OFF: Using L2OFF geodata files (filename e.g. 22_16_conv.dat) PathnodePath = ./data/pathnode/
GeoDataType = L2J
# ================================================================= # Pathfinding array buffers configuration.
# Pathfinding PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
# =================================================================
# When line of movement check fails, the pathfinding algoritm is performed to look for # Weight for nodes without obstacles far from walls.
# an alternative path (e.g. walk around obstacle), default: True LowWeight = 0.5
PathFinding = True
# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 # Weight for nodes near walls.
PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 MediumWeight = 2
# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 # Weight for nodes with obstacles.
MoveWeight = 10 HighWeight = 3
MoveWeightDiag = 14
# When movement flags of target node is blocked to any direction, use this weight instead of MoveWeight or MoveWeightDiag. # Angle paths will be more "smart", but in cost of higher CPU utilization.
# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 30 AdvancedDiagonalStrategy = True
ObstacleWeight = 30
# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 12 and 18 # Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True
# For proper function must be higher than MoveWeight. # Default: LowWeight * sqrt(2)
HeuristicWeight = 12 DiagonalWeight = 0.707
HeuristicWeightDiag = 18
# Maximum number of generated nodes per one path-finding process, default 3500 # Maximum number of LOS postfilter passes, 0 will disable postfilter.
MaxIterations = 3500 # Default: 3
MaxPostfilterPasses = 3
# ================================================================= # Path debug function.
# Line of Sight # Nodes known to pathfinder will be displayed as adena, constructed path as antidots.
# ================================================================= # Number of the items show node cost * 10
# Potions display path after first stage filter
# Line of sight start at X percent of the character height, default: 75 # Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter
PartOfCharacterHeight = 75 # This function FOR DEBUG PURPOSES ONLY, never use it on the live server!
DebugPath = False
# Maximum height of an obstacle, which can exceed the line of sight, default: 32
MaxObstacleHeight = 32

View File

@@ -25,6 +25,3 @@ b - Make it work
To make geodata work: To make geodata work:
* unpack your geodata files into "/data/geodata" folder (or any other folder) * unpack your geodata files into "/data/geodata" folder (or any other folder)
* open "/config/GeoEngine.ini" with your favorite text editor and then edit following configs:
- GeoDataPath = set path to your geodata, if elsewhere than "./data/geodata/"
- GeoDataType = set the geodata format, which you are using.

View File

@@ -55,8 +55,8 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX(); final int worldX = activeChar.getX();
final int worldY = activeChar.getY(); final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ(); final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX); final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY); final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{ {
@@ -73,8 +73,8 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX(); final int worldX = activeChar.getX();
final int worldY = activeChar.getY(); final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ(); final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX); final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY); final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{ {

View File

@@ -19,9 +19,9 @@ package handlers.admincommandhandlers;
import java.util.List; import java.util.List;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler; import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.util.BuilderUtil; import org.l2jmobius.gameserver.util.BuilderUtil;
@@ -37,20 +37,21 @@ public class AdminPathNode implements IAdminCommandHandler
{ {
if (command.equals("admin_path_find")) if (command.equals("admin_path_find"))
{ {
if (!Config.PATHFINDING) if (Config.PATHFINDING < 1)
{ {
BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled.");
return true; return true;
} }
if (activeChar.getTarget() != null) if (activeChar.getTarget() != null)
{ {
final List<Location> path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); final List<AbstractNodeLoc> path = PathFinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld(), true);
if (path == null) if (path == null)
{ {
BuilderUtil.sendSysMessage(activeChar, "No Route!"); BuilderUtil.sendSysMessage(activeChar, "No Route!");
return true; return true;
} }
for (Location a : path) for (AbstractNodeLoc a : path)
{ {
BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ());
} }

View File

@@ -72,7 +72,7 @@ public class AdminServerInfo implements IAdminCommandHandler
html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS); html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS);
html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute()); html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute());
html.replace("%dayNight%", GameTimeTaskManager.getInstance().isNight() ? "Night" : "Day"); html.replace("%dayNight%", GameTimeTaskManager.getInstance().isNight() ? "Night" : "Day");
html.replace("%geodata%", Config.PATHFINDING ? "Enabled" : "Disabled"); html.replace("%geodata%", Config.PATHFINDING > 0 ? "Enabled" : "Disabled");
html.replace("%serverTime%", SDF.format(new Date(System.currentTimeMillis()))); html.replace("%serverTime%", SDF.format(new Date(System.currentTimeMillis())));
html.replace("%serverUpTime%", getServerUpTime()); html.replace("%serverUpTime%", getServerUpTime());
html.replace("%onlineAll%", getPlayersCount("ALL")); html.replace("%onlineAll%", getPlayersCount("ALL"));

View File

@@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler
return null; return null;
} }
if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition))
{ {
if (sendMessage) if (sendMessage)
{ {

View File

@@ -61,7 +61,6 @@ import org.l2jmobius.commons.util.PropertiesParser;
import org.l2jmobius.commons.util.StringUtil; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.enums.ClassId; import org.l2jmobius.gameserver.enums.ClassId;
import org.l2jmobius.gameserver.enums.GeoType;
import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType; import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType;
import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder;
@@ -967,18 +966,16 @@ public class Config
// GeoEngine // GeoEngine
// -------------------------------------------------- // --------------------------------------------------
public static Path GEODATA_PATH; public static Path GEODATA_PATH;
public static GeoType GEODATA_TYPE; public static Path PATHNODE_PATH;
public static boolean PATHFINDING; public static int PATHFINDING;
public static String PATHFIND_BUFFERS; public static String PATHFIND_BUFFERS;
public static int MOVE_WEIGHT; public static float LOW_WEIGHT;
public static int MOVE_WEIGHT_DIAG; public static float MEDIUM_WEIGHT;
public static int OBSTACLE_WEIGHT; public static float HIGH_WEIGHT;
public static int OBSTACLE_WEIGHT_DIAG; public static boolean ADVANCED_DIAGONAL_STRATEGY;
public static int HEURISTIC_WEIGHT; public static float DIAGONAL_WEIGHT;
public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_POSTFILTER_PASSES;
public static int MAX_ITERATIONS; public static boolean DEBUG_PATH;
public static int PART_OF_CHARACTER_HEIGHT;
public static int MAX_OBSTACLE_HEIGHT;
/** Attribute System */ /** Attribute System */
public static int S_WEAPON_STONE; public static int S_WEAPON_STONE;
@@ -2525,19 +2522,17 @@ public class Config
// Load GeoEngine config file (if exists) // Load GeoEngine config file (if exists)
final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE);
GEODATA_PATH = Paths.get(geoEngineConfig.getString("GeoDataPath", "./data/geodata")); GEODATA_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("GeoDataPath", "geodata"));
GEODATA_TYPE = Enum.valueOf(GeoType.class, geoEngineConfig.getString("GeoDataType", "L2J")); PATHNODE_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("PathnodePath", "pathnode"));
PATHFINDING = geoEngineConfig.getBoolean("PathFinding", true); PATHFINDING = geoEngineConfig.getInt("PathFinding", 0);
PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
MOVE_WEIGHT = geoEngineConfig.getInt("MoveWeight", 10); LOW_WEIGHT = geoEngineConfig.getFloat("LowWeight", 0.5f);
MOVE_WEIGHT_DIAG = geoEngineConfig.getInt("MoveWeightDiag", 14); MEDIUM_WEIGHT = geoEngineConfig.getFloat("MediumWeight", 2);
OBSTACLE_WEIGHT = geoEngineConfig.getInt("ObstacleWeight", 30); HIGH_WEIGHT = geoEngineConfig.getFloat("HighWeight", 3);
OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); ADVANCED_DIAGONAL_STRATEGY = geoEngineConfig.getBoolean("AdvancedDiagonalStrategy", true);
HEURISTIC_WEIGHT = geoEngineConfig.getInt("HeuristicWeight", 12); DIAGONAL_WEIGHT = geoEngineConfig.getFloat("DiagonalWeight", 0.707f);
HEURISTIC_WEIGHT_DIAG = geoEngineConfig.getInt("HeuristicWeightDiag", 18); MAX_POSTFILTER_PASSES = geoEngineConfig.getInt("MaxPostfilterPasses", 3);
MAX_ITERATIONS = geoEngineConfig.getInt("MaxIterations", 3500); DEBUG_PATH = geoEngineConfig.getBoolean("DebugPath", false);
PART_OF_CHARACTER_HEIGHT = geoEngineConfig.getInt("PartOfCharacterHeight", 75);
MAX_OBSTACLE_HEIGHT = geoEngineConfig.getInt("MaxObstacleHeight", 32);
// Load AllowedPlayerRaces config file (if exists) // Load AllowedPlayerRaces config file (if exists)
final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE);

View File

@@ -1,144 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.enums;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
/**
* Container of movement constants used for various geodata and movement checks.
*/
public enum MoveDirectionType
{
N(0, -1),
S(0, 1),
W(-1, 0),
E(1, 0),
NW(-1, -1),
SW(-1, 1),
NE(1, -1),
SE(1, 1);
// Step and signum.
private final int _stepX;
private final int _stepY;
private final int _signumX;
private final int _signumY;
// Cell offset.
private final int _offsetX;
private final int _offsetY;
// Direction flags.
private final byte _directionX;
private final byte _directionY;
private final String _symbolX;
private final String _symbolY;
private MoveDirectionType(int signumX, int signumY)
{
// Get step (world -16, 0, 16) and signum (geodata -1, 0, 1) coordinates.
_stepX = signumX * GeoStructure.CELL_SIZE;
_stepY = signumY * GeoStructure.CELL_SIZE;
_signumX = signumX;
_signumY = signumY;
// Get border offsets in a direction of iteration.
_offsetX = signumX >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
_offsetY = signumY >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
// Get direction NSWE flag and symbol.
_directionX = signumX < 0 ? GeoStructure.CELL_FLAG_W : signumX == 0 ? 0 : GeoStructure.CELL_FLAG_E;
_directionY = signumY < 0 ? GeoStructure.CELL_FLAG_N : signumY == 0 ? 0 : GeoStructure.CELL_FLAG_S;
_symbolX = signumX < 0 ? "W" : signumX == 0 ? "-" : "E";
_symbolY = signumY < 0 ? "N" : signumY == 0 ? "-" : "S";
}
public int getStepX()
{
return _stepX;
}
public int getStepY()
{
return _stepY;
}
public int getSignumX()
{
return _signumX;
}
public int getSignumY()
{
return _signumY;
}
public int getOffsetX()
{
return _offsetX;
}
public int getOffsetY()
{
return _offsetY;
}
public byte getDirectionX()
{
return _directionX;
}
public byte getDirectionY()
{
return _directionY;
}
public String getSymbolX()
{
return _symbolX;
}
public String getSymbolY()
{
return _symbolY;
}
/**
* @param gdx : Geodata X delta coordinate.
* @param gdy : Geodata Y delta coordinate.
* @return {@link MoveDirectionType} based on given geodata dx and dy delta coordinates.
*/
public static MoveDirectionType getDirection(int gdx, int gdy)
{
if (gdx == 0)
{
return (gdy < 0) ? MoveDirectionType.N : MoveDirectionType.S;
}
if (gdy == 0)
{
return (gdx < 0) ? MoveDirectionType.W : MoveDirectionType.E;
}
if (gdx > 0)
{
return (gdy < 0) ? MoveDirectionType.NE : MoveDirectionType.SE;
}
return (gdy < 0) ? MoveDirectionType.NW : MoveDirectionType.SW;
}
}

View File

@@ -1,85 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
public abstract class ABlock
{
/**
* Checks the block for having geodata.
* @return boolean : True, when block has geodata (Flat, Complex, Multilayer).
*/
public abstract boolean hasGeoPos();
/**
* Returns the height of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, nearest to given coordinates.
*/
public abstract short getHeightNearest(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is closes layer to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index.
*/
public abstract int getIndexNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer above given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexAbove(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer below given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexBelow(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeight(int index);
/**
* Returns the NSWE flag byte of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract byte getNswe(int index);
}

View File

@@ -1,130 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
public class BlockComplex extends ABlock
{
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockComplex()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates ComplexBlock.
* @param bb : Input byte buffer.
*/
public BlockComplex(ByteBuffer bb)
{
// Initialize buffer.
_buffer = new byte[GeoStructure.BLOCK_CELLS * 3];
// Load data.
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
{
// Get data.
short data = bb.getShort();
// Get nswe.
_buffer[i * 3] = (byte) (data & 0x000F);
// Get height.
data = (short) ((short) (data & 0xFFF0) >> 1);
_buffer[(i * 3) + 1] = (byte) (data & 0x00FF);
_buffer[(i * 3) + 2] = (byte) (data >> 8);
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get nswe.
return _buffer[index];
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height > worldZ ? index : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height < worldZ ? index : -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@@ -1,95 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockFlat extends ABlock
{
protected final short _height;
protected byte _nswe;
/**
* Creates FlatBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockFlat(ByteBuffer bb, GeoType type)
{
// Get height and nswe.
_height = bb.getShort();
_nswe = GeoStructure.CELL_FLAG_ALL;
// Read dummy data.
if (type == GeoType.L2OFF)
{
bb.getShort();
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height > worldZ ? 0 : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height < worldZ ? 0 : -1;
}
@Override
public short getHeight(int index)
{
return _height;
}
@Override
public byte getNswe(int index)
{
return _nswe;
}
}

View File

@@ -1,248 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockMultilayer extends ABlock
{
private static final int MAX_LAYERS = Byte.MAX_VALUE;
private static ByteBuffer _temp;
/**
* Initializes the temporary buffer.
*/
public static final void initialize()
{
// Initialize temporary buffer and sorting mechanism.
_temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3);
_temp.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Releases temporary buffer.
*/
public static final void release()
{
_temp = null;
}
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockMultilayer()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates MultilayerBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockMultilayer(ByteBuffer bb, GeoType type)
{
// Move buffer pointer to end of MultilayerBlock.
for (int cell = 0; cell < GeoStructure.BLOCK_CELLS; cell++)
{
// Get layer count for this cell.
final byte layers = type != GeoType.L2OFF ? bb.get() : (byte) bb.getShort();
if ((layers <= 0) || (layers > MAX_LAYERS))
{
throw new RuntimeException("Invalid layer count for MultilayerBlock");
}
// Add layers count.
_temp.put(layers);
// Loop over layers.
for (byte layer = 0; layer < layers; layer++)
{
// Get data.
final short data = bb.getShort();
// Add nswe and height.
_temp.put((byte) (data & 0x000F));
_temp.putShort((short) ((short) (data & 0xFFF0) >> 1));
}
}
// Initialize buffer.
_buffer = Arrays.copyOf(_temp.array(), _temp.position());
// Clear temp buffer.
_temp.clear();
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get nswe.
return _buffer[index];
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
// Loop though all cell layers, find closest layer to given worldZ.
int limit = Integer.MAX_VALUE;
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Get Z distance and compare with limit.
// Note: When 2 layers have same distance to worldZ (worldZ is in the middle of them):
// > Returns bottom layer.
// >= Returns upper layer.
final int distance = Math.abs(height - worldZ);
if (distance > limit)
{
break;
}
// Update limit and move to next layer.
limit = distance;
index += 3;
}
// Return layer index.
return index - 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// Loop though all layers, find first layer above worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is higher than worldZ, return layer index.
if (height > worldZ)
{
return index;
}
// Move index to next layer.
index -= 3;
}
// No layer found.
return -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to first layer data (first from top).
byte layers = _buffer[index++];
// Loop though all layers, find first layer below worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is lower than worldZ, return layer index.
if (height < worldZ)
{
return index;
}
// Move index to next layer.
index += 3;
}
// No layer found.
return -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public final class Cell
{
/** East NSWE flag */
public static final byte NSWE_EAST = 1 << 0;
/** West NSWE flag */
public static final byte NSWE_WEST = 1 << 1;
/** South NSWE flag */
public static final byte NSWE_SOUTH = 1 << 2;
/** North NSWE flag */
public static final byte NSWE_NORTH = 1 << 3;
/** North-East NSWE flags */
public static final byte NSWE_NORTH_EAST = NSWE_NORTH | NSWE_EAST;
/** North-West NSWE flags */
public static final byte NSWE_NORTH_WEST = NSWE_NORTH | NSWE_WEST;
/** South-East NSWE flags */
public static final byte NSWE_SOUTH_EAST = NSWE_SOUTH | NSWE_EAST;
/** South-West NSWE flags */
public static final byte NSWE_SOUTH_WEST = NSWE_SOUTH | NSWE_WEST;
/** All directions NSWE flags */
public static final byte NSWE_ALL = NSWE_EAST | NSWE_WEST | NSWE_SOUTH | NSWE_NORTH;
private Cell()
{
}
}

View File

@@ -0,0 +1,162 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.l2jmobius.gameserver.geoengine.geodata.regions.NullRegion;
import org.l2jmobius.gameserver.geoengine.geodata.regions.Region;
/**
* @author HorridoJoho
*/
public final class GeoData
{
// world dimensions: 1048576 * 1048576 = 1099511627776
private static final int WORLD_MIN_X = -655360;
private static final int WORLD_MAX_X = 393215;
private static final int WORLD_MIN_Y = -589824;
private static final int WORLD_MAX_Y = 458751;
/** Regions in the world on the x axis */
public static final int GEO_REGIONS_X = 32;
/** Regions in the world on the y axis */
public static final int GEO_REGIONS_Y = 32;
/** Region in the world */
public static final int GEO_REGIONS = GEO_REGIONS_X * GEO_REGIONS_Y;
/** Blocks in the world on the x axis */
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * IRegion.REGION_BLOCKS_X;
/** Blocks in the world on the y axis */
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * IRegion.REGION_BLOCKS_Y;
/** Blocks in the world */
public static final int GEO_BLOCKS = GEO_REGIONS * IRegion.REGION_BLOCKS;
/** Cells in the world on the x axis */
public static final int GEO_CELLS_X = GEO_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in the world in the y axis */
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** The regions array */
private final AtomicReferenceArray<IRegion> _regions = new AtomicReferenceArray<>(GEO_REGIONS);
public GeoData()
{
for (int i = 0; i < _regions.length(); i++)
{
_regions.set(i, NullRegion.INSTANCE);
}
}
private void checkGeoX(int geoX)
{
if ((geoX < 0) || (geoX >= GEO_CELLS_X))
{
throw new IllegalArgumentException();
}
}
private void checkGeoY(int geoY)
{
if ((geoY < 0) || (geoY >= GEO_CELLS_Y))
{
throw new IllegalArgumentException();
}
}
private IRegion getRegion(int geoX, int geoY)
{
checkGeoX(geoX);
checkGeoY(geoY);
return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y));
}
public void loadRegion(Path filePath, int regionX, int regionY) throws IOException
{
final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY;
try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r"))
{
_regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).order(ByteOrder.LITTLE_ENDIAN)));
}
}
public void unloadRegion(int regionX, int regionY)
{
_regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE);
}
public boolean hasGeoPos(int geoX, int geoY)
{
return getRegion(geoX, geoY).hasGeo();
}
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
public int getGeoX(int worldX)
{
if ((worldX < WORLD_MIN_X) || (worldX > WORLD_MAX_X))
{
throw new IllegalArgumentException();
}
return (worldX - WORLD_MIN_X) / 16;
}
public int getGeoY(int worldY)
{
if ((worldY < WORLD_MIN_Y) || (worldY > WORLD_MAX_Y))
{
throw new IllegalArgumentException();
}
return (worldY - WORLD_MIN_Y) / 16;
}
public int getWorldX(int geoX)
{
checkGeoX(geoX);
return (geoX * 16) + WORLD_MIN_X + 8;
}
public int getWorldY(int geoY)
{
checkGeoY(geoY);
return (geoY * 16) + WORLD_MIN_Y + 8;
}
}

View File

@@ -1,81 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
import org.l2jmobius.gameserver.model.World;
public final class GeoStructure
{
// Geo cell direction (nswe) flags.
public static final byte CELL_FLAG_NONE = 0x00;
public static final byte CELL_FLAG_E = 0x01;
public static final byte CELL_FLAG_W = 0x02;
public static final byte CELL_FLAG_S = 0x04;
public static final byte CELL_FLAG_N = 0x08;
public static final byte CELL_FLAG_SE = 0x10;
public static final byte CELL_FLAG_SW = 0x20;
public static final byte CELL_FLAG_NE = 0x40;
public static final byte CELL_FLAG_NW = (byte) 0x80;
public static final byte CELL_FLAG_ALL = 0x0F;
// Geo cell expansion flags
public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW;
public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE;
public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E;
public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W;
public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E;
public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W;
// public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW;
public static final byte CELL_EXPANSION_ALL = (byte) 0xFF;
// Geo cell height constants.
public static final int CELL_SIZE = 16;
public static final int CELL_HEIGHT = 8;
public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6;
// Geo block type identification.
public static final byte TYPE_FLAT_L2J_L2OFF = 0;
public static final byte TYPE_COMPLEX_L2J = 1;
public static final byte TYPE_COMPLEX_L2OFF = 0x40;
public static final byte TYPE_MULTILAYER_L2J = 2;
// public static final byte TYPE_MULTILAYER_L2OFF = 0x41; // officially not does exist, is anything above complex block (0x41 - 0xFFFF)
// Geo block dimensions.
public static final int BLOCK_CELLS_X = 8;
public static final int BLOCK_CELLS_Y = 8;
public static final int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
// Geo region dimensions.
public static final int REGION_BLOCKS_X = 256;
public static final int REGION_BLOCKS_Y = 256;
public static final int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
public static final int REGION_CELLS_X = REGION_BLOCKS_X * BLOCK_CELLS_X;
public static final int REGION_CELLS_Y = REGION_BLOCKS_Y * BLOCK_CELLS_Y;
// Geo world dimensions.
public static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1);
public static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.TILE_Y_MIN) + 1);
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * REGION_BLOCKS_X;
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * REGION_BLOCKS_Y;
public static final int GEO_CELLS_X = GEO_BLOCKS_X * BLOCK_CELLS_X;
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * BLOCK_CELLS_Y;
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IBlock
{
int TYPE_FLAT = 0;
int TYPE_COMPLEX = 1;
int TYPE_MULTILAYER = 2;
/** Cells in a block on the x axis */
int BLOCK_CELLS_X = 8;
/** Cells in a block on the y axis */
int BLOCK_CELLS_Y = 8;
/** Cells in a block */
int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
}

Some files were not shown because too many files have changed in this diff Show More