Abstraction Layer GeoEngine.
This commit is contained in:
		| @@ -294,7 +294,6 @@ CorrectPrices = True | ||||
| # --------------------------------------------------------------------------- | ||||
|  | ||||
| # Allow characters to receive damage from falling. | ||||
| # CoordSynchronize = 2 is recommended. | ||||
| # Default: True | ||||
| EnableFallingDamage = True | ||||
|  | ||||
|   | ||||
| @@ -2,48 +2,47 @@ | ||||
| #                             Geodata | ||||
| # ================================================================= | ||||
|  | ||||
| # Specifies the path to geodata files. For example, when using geodata files located | ||||
| # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ | ||||
| # Pathfinding options: | ||||
| # 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/ | ||||
|  | ||||
| # Specifies the geodata files type. Default: L2J | ||||
| # L2J: Using L2J geodata files (filename e.g. 22_16.l2j) | ||||
| # L2OFF: Using L2OFF geodata files (filename e.g. 22_16_conv.dat) | ||||
| GeoDataType = L2J | ||||
| # Pathnode file directory. | ||||
| # Default: pathnode | ||||
| PathnodePath = ./data/pathnode/ | ||||
|  | ||||
| # ================================================================= | ||||
| #                           Pathfinding | ||||
| # ================================================================= | ||||
| # Pathfinding array buffers configuration. | ||||
| PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 | ||||
|  | ||||
| # When line of movement check fails, the pathfinding algoritm is performed to look for | ||||
| # an alternative path (e.g. walk around obstacle), default: True | ||||
| PathFinding = True | ||||
| # Weight for nodes without obstacles far from walls. | ||||
| LowWeight = 0.5 | ||||
|  | ||||
| # Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 | ||||
| PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 | ||||
| # Weight for nodes near walls. | ||||
| MediumWeight = 2 | ||||
|  | ||||
| # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 | ||||
| MoveWeight = 10 | ||||
| MoveWeightDiag = 14 | ||||
| # Weight for nodes with obstacles. | ||||
| HighWeight = 3 | ||||
|  | ||||
| # When movement flags of target node is blocked to any direction, use this weight instead of MoveWeight or MoveWeightDiag. | ||||
| # This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 30 | ||||
| ObstacleWeight = 30 | ||||
| # Angle paths will be more "smart", but in cost of higher CPU utilization. | ||||
| AdvancedDiagonalStrategy = True | ||||
|  | ||||
| # Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 12 and 18 | ||||
| # For proper function must be higher than MoveWeight. | ||||
| HeuristicWeight = 12 | ||||
| HeuristicWeightDiag = 18 | ||||
| # Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True | ||||
| # Default: LowWeight * sqrt(2)  | ||||
| DiagonalWeight = 0.707 | ||||
|  | ||||
| # Maximum number of generated nodes per one path-finding process, default 3500 | ||||
| MaxIterations = 3500 | ||||
| # Maximum number of LOS postfilter passes, 0 will disable postfilter. | ||||
| # Default: 3 | ||||
| MaxPostfilterPasses = 3 | ||||
|  | ||||
| # ================================================================= | ||||
| #                           Line of Sight | ||||
| # ================================================================= | ||||
|  | ||||
| # Line of sight start at X percent of the character height, default: 75 | ||||
| PartOfCharacterHeight = 75 | ||||
|  | ||||
| # Maximum height of an obstacle, which can exceed the line of sight, default: 32 | ||||
| MaxObstacleHeight = 32 | ||||
| # Path debug function. | ||||
| # 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 | ||||
| # Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter | ||||
| # This function FOR DEBUG PURPOSES ONLY, never use it on the live server! | ||||
| DebugPath = False | ||||
|   | ||||
| @@ -25,6 +25,3 @@ b - Make it work | ||||
|  | ||||
| To make geodata work: | ||||
| * 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. | ||||
|   | ||||
| @@ -55,8 +55,8 @@ public class AdminGeodata implements IAdminCommandHandler | ||||
| 				final int worldX = activeChar.getX(); | ||||
| 				final int worldY = activeChar.getY(); | ||||
| 				final int worldZ = activeChar.getZ(); | ||||
| 				final int geoX = GeoEngine.getGeoX(worldX); | ||||
| 				final int geoY = GeoEngine.getGeoY(worldY); | ||||
| 				final int geoX = GeoEngine.getInstance().getGeoX(worldX); | ||||
| 				final int geoY = GeoEngine.getInstance().getGeoY(worldY); | ||||
| 				 | ||||
| 				if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) | ||||
| 				{ | ||||
| @@ -73,8 +73,8 @@ public class AdminGeodata implements IAdminCommandHandler | ||||
| 				final int worldX = activeChar.getX(); | ||||
| 				final int worldY = activeChar.getY(); | ||||
| 				final int worldZ = activeChar.getZ(); | ||||
| 				final int geoX = GeoEngine.getGeoX(worldX); | ||||
| 				final int geoY = GeoEngine.getGeoY(worldY); | ||||
| 				final int geoX = GeoEngine.getInstance().getGeoX(worldX); | ||||
| 				final int geoY = GeoEngine.getInstance().getGeoY(worldY); | ||||
| 				 | ||||
| 				if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) | ||||
| 				{ | ||||
|   | ||||
| @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; | ||||
| import java.util.List; | ||||
|  | ||||
| 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.model.Location; | ||||
| import org.l2jmobius.gameserver.model.actor.Player; | ||||
| import org.l2jmobius.gameserver.util.BuilderUtil; | ||||
|  | ||||
| @@ -37,20 +37,21 @@ public class AdminPathNode implements IAdminCommandHandler | ||||
| 	{ | ||||
| 		if (command.equals("admin_path_find")) | ||||
| 		{ | ||||
| 			if (!Config.PATHFINDING) | ||||
| 			if (Config.PATHFINDING < 1) | ||||
| 			{ | ||||
| 				BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); | ||||
| 				return true; | ||||
| 			} | ||||
| 			 | ||||
| 			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) | ||||
| 				{ | ||||
| 					BuilderUtil.sendSysMessage(activeChar, "No Route!"); | ||||
| 					return true; | ||||
| 				} | ||||
| 				for (Location a : path) | ||||
| 				for (AbstractNodeLoc a : path) | ||||
| 				{ | ||||
| 					BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); | ||||
| 				} | ||||
|   | ||||
| @@ -72,7 +72,7 @@ public class AdminServerInfo implements IAdminCommandHandler | ||||
| 			html.replace("%slots%", getPlayersCount("ALL") + "/" + Config.MAXIMUM_ONLINE_USERS); | ||||
| 			html.replace("%gameTime%", GameTimeTaskManager.getInstance().getGameHour() + ":" + GameTimeTaskManager.getInstance().getGameMinute()); | ||||
| 			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("%serverUpTime%", getServerUpTime()); | ||||
| 			html.replace("%onlineAll%", getPlayersCount("ALL")); | ||||
|   | ||||
| @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler | ||||
| 					return null; | ||||
| 				} | ||||
| 				 | ||||
| 				if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) | ||||
| 				if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) | ||||
| 				{ | ||||
| 					if (sendMessage) | ||||
| 					{ | ||||
|   | ||||
| @@ -61,7 +61,6 @@ import org.l2jmobius.commons.util.PropertiesParser; | ||||
| import org.l2jmobius.commons.util.StringUtil; | ||||
| import org.l2jmobius.gameserver.enums.ChatType; | ||||
| import org.l2jmobius.gameserver.enums.ClassId; | ||||
| import org.l2jmobius.gameserver.enums.GeoType; | ||||
| import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType; | ||||
| import org.l2jmobius.gameserver.model.Location; | ||||
| import org.l2jmobius.gameserver.model.holders.ItemHolder; | ||||
| @@ -907,18 +906,16 @@ public class Config | ||||
| 	// GeoEngine | ||||
| 	// -------------------------------------------------- | ||||
| 	public static Path GEODATA_PATH; | ||||
| 	public static GeoType GEODATA_TYPE; | ||||
| 	public static boolean PATHFINDING; | ||||
| 	public static Path PATHNODE_PATH; | ||||
| 	public static int PATHFINDING; | ||||
| 	public static String PATHFIND_BUFFERS; | ||||
| 	public static int MOVE_WEIGHT; | ||||
| 	public static int MOVE_WEIGHT_DIAG; | ||||
| 	public static int OBSTACLE_WEIGHT; | ||||
| 	public static int OBSTACLE_WEIGHT_DIAG; | ||||
| 	public static int HEURISTIC_WEIGHT; | ||||
| 	public static int HEURISTIC_WEIGHT_DIAG; | ||||
| 	public static int MAX_ITERATIONS; | ||||
| 	public static int PART_OF_CHARACTER_HEIGHT; | ||||
| 	public static int MAX_OBSTACLE_HEIGHT; | ||||
| 	public static float LOW_WEIGHT; | ||||
| 	public static float MEDIUM_WEIGHT; | ||||
| 	public static float HIGH_WEIGHT; | ||||
| 	public static boolean ADVANCED_DIAGONAL_STRATEGY; | ||||
| 	public static float DIAGONAL_WEIGHT; | ||||
| 	public static int MAX_POSTFILTER_PASSES; | ||||
| 	public static boolean DEBUG_PATH; | ||||
| 	 | ||||
| 	/** Attribute System */ | ||||
| 	public static int S_WEAPON_STONE; | ||||
| @@ -2415,19 +2412,17 @@ public class Config | ||||
| 			 | ||||
| 			// Load GeoEngine config file (if exists) | ||||
| 			final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); | ||||
| 			GEODATA_PATH = Paths.get(geoEngineConfig.getString("GeoDataPath", "./data/geodata")); | ||||
| 			GEODATA_TYPE = Enum.valueOf(GeoType.class, geoEngineConfig.getString("GeoDataType", "L2J")); | ||||
| 			PATHFINDING = geoEngineConfig.getBoolean("PathFinding", true); | ||||
| 			PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); | ||||
| 			MOVE_WEIGHT = geoEngineConfig.getInt("MoveWeight", 10); | ||||
| 			MOVE_WEIGHT_DIAG = geoEngineConfig.getInt("MoveWeightDiag", 14); | ||||
| 			OBSTACLE_WEIGHT = geoEngineConfig.getInt("ObstacleWeight", 30); | ||||
| 			OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); | ||||
| 			HEURISTIC_WEIGHT = geoEngineConfig.getInt("HeuristicWeight", 12); | ||||
| 			HEURISTIC_WEIGHT_DIAG = geoEngineConfig.getInt("HeuristicWeightDiag", 18); | ||||
| 			MAX_ITERATIONS = geoEngineConfig.getInt("MaxIterations", 3500); | ||||
| 			PART_OF_CHARACTER_HEIGHT = geoEngineConfig.getInt("PartOfCharacterHeight", 75); | ||||
| 			MAX_OBSTACLE_HEIGHT = geoEngineConfig.getInt("MaxObstacleHeight", 32); | ||||
| 			GEODATA_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("GeoDataPath", "geodata")); | ||||
| 			PATHNODE_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("PathnodePath", "pathnode")); | ||||
| 			PATHFINDING = geoEngineConfig.getInt("PathFinding", 0); | ||||
| 			PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); | ||||
| 			LOW_WEIGHT = geoEngineConfig.getFloat("LowWeight", 0.5f); | ||||
| 			MEDIUM_WEIGHT = geoEngineConfig.getFloat("MediumWeight", 2); | ||||
| 			HIGH_WEIGHT = geoEngineConfig.getFloat("HighWeight", 3); | ||||
| 			ADVANCED_DIAGONAL_STRATEGY = geoEngineConfig.getBoolean("AdvancedDiagonalStrategy", true); | ||||
| 			DIAGONAL_WEIGHT = geoEngineConfig.getFloat("DiagonalWeight", 0.707f); | ||||
| 			MAX_POSTFILTER_PASSES = geoEngineConfig.getInt("MaxPostfilterPasses", 3); | ||||
| 			DEBUG_PATH = geoEngineConfig.getBoolean("DebugPath", false); | ||||
| 			 | ||||
| 			// Load AllowedPlayerRaces config file (if exists) | ||||
| 			final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_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; | ||||
| 	} | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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); | ||||
| } | ||||
| @@ -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]; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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]; | ||||
| 	} | ||||
| } | ||||
| @@ -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() | ||||
| 	{ | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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); | ||||
| } | ||||
| @@ -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(); | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -14,55 +14,44 @@ | ||||
|  * 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; | ||||
| 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 | ||||
| 	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; | ||||
| 	} | ||||
| 	 | ||||
| 	@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; | ||||
| 	} | ||||
| } | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -14,22 +14,22 @@ | ||||
|  * 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; | ||||
| package org.l2jmobius.gameserver.geoengine.pathfinding; | ||||
| 
 | ||||
| public enum GeoType | ||||
| /** | ||||
|  * @author -Nemesiss- | ||||
|  */ | ||||
| public abstract class AbstractNodeLoc | ||||
| { | ||||
| 	L2J("%d_%d.l2j"), | ||||
| 	L2OFF("%d_%d_conv.dat"); | ||||
| 	public abstract int getX(); | ||||
| 	 | ||||
| 	private final String _filename; | ||||
| 	public abstract int getY(); | ||||
| 	 | ||||
| 	private GeoType(String filename) | ||||
| 	{ | ||||
| 		_filename = filename; | ||||
| 	} | ||||
| 	public abstract int getZ(); | ||||
| 	 | ||||
| 	public String getFilename() | ||||
| 	{ | ||||
| 		return _filename; | ||||
| 	} | ||||
| } | ||||
| 	public abstract void setZ(short z); | ||||
| 	 | ||||
| 	public abstract int getNodeX(); | ||||
| 	 | ||||
| 	public abstract int getNodeY(); | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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); | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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(); | ||||
| 	} | ||||
| } | ||||
| @@ -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); | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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); | ||||
| 	} | ||||
| } | ||||
| @@ -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(); | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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 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()) // | ||||
| 				&& 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; | ||||
| 				newlocy = randY; | ||||
|   | ||||
| @@ -68,6 +68,8 @@ import org.l2jmobius.gameserver.enums.Team; | ||||
| import org.l2jmobius.gameserver.enums.TeleportWhereType; | ||||
| import org.l2jmobius.gameserver.enums.UserInfoType; | ||||
| 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.MapRegionManager; | ||||
| import org.l2jmobius.gameserver.instancemanager.QuestManager; | ||||
| @@ -822,7 +824,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe | ||||
| 	{ | ||||
| 		int x = xValue; | ||||
| 		int y = yValue; | ||||
| 		int z = zValue; | ||||
| 		int z = _isFlying ? zValue : GeoEngine.getInstance().getHeight(x, y, zValue); | ||||
| 		int heading = headingValue; | ||||
| 		Instance instance = instanceValue; | ||||
| 		 | ||||
| @@ -2689,7 +2691,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe | ||||
| 		 | ||||
| 		public boolean disregardingGeodata; | ||||
| 		public int onGeodataPathIndex; | ||||
| 		public List<Location> geoPath; | ||||
| 		public List<AbstractNodeLoc> geoPath; | ||||
| 		public int geoPathAccurateTx; | ||||
| 		public int geoPathAccurateTy; | ||||
| 		public int geoPathGtx; | ||||
| @@ -3478,7 +3480,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe | ||||
| 			} | ||||
| 			 | ||||
| 			// Movement checks. | ||||
| 			if (Config.PATHFINDING && !(this instanceof FriendlyNpc)) | ||||
| 			if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc)) | ||||
| 			{ | ||||
| 				final double originalDistance = distance; | ||||
| 				final int originalX = x; | ||||
| @@ -3522,7 +3524,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe | ||||
| 				if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) | ||||
| 				{ | ||||
| 					// 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 (isPlayer() && !_isFlying && !isInWater) | ||||
| @@ -3566,7 +3568,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe | ||||
| 			} | ||||
| 			 | ||||
| 			// 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()) | ||||
| 				{ | ||||
|   | ||||
| @@ -19,7 +19,7 @@ package org.l2jmobius.gameserver.util; | ||||
| import java.awt.Color; | ||||
|  | ||||
| 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.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) | ||||
| 	{ | ||||
| 		final int gx = GeoEngine.getGeoX(x); | ||||
| 		final int gy = GeoEngine.getGeoY(y); | ||||
| 		final int gx = GeoEngine.getInstance().getGeoX(x); | ||||
| 		final int gy = GeoEngine.getInstance().getGeoY(y); | ||||
| 		 | ||||
| 		final int tgx = GeoEngine.getGeoX(tx); | ||||
| 		final int tgy = GeoEngine.getGeoY(ty); | ||||
| 		final int tgx = GeoEngine.getInstance().getGeoX(tx); | ||||
| 		final int tgy = GeoEngine.getInstance().getGeoY(ty); | ||||
| 		 | ||||
| 		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); | ||||
| 		 | ||||
| 		while (iter.next()) | ||||
| 		{ | ||||
| 			final int wx = GeoEngine.getWorldX(iter.x()); | ||||
| 			final int wy = GeoEngine.getWorldY(iter.y()); | ||||
| 			final int wx = GeoEngine.getInstance().getWorldX(iter.x()); | ||||
| 			final int wy = GeoEngine.getInstance().getWorldY(iter.y()); | ||||
| 			 | ||||
| 			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) | ||||
| 	{ | ||||
| 		final int gx = GeoEngine.getGeoX(x); | ||||
| 		final int gy = GeoEngine.getGeoY(y); | ||||
| 		final int gx = GeoEngine.getInstance().getGeoX(x); | ||||
| 		final int gy = GeoEngine.getInstance().getGeoY(y); | ||||
| 		 | ||||
| 		final int tgx = GeoEngine.getGeoX(tx); | ||||
| 		final int tgy = GeoEngine.getGeoY(ty); | ||||
| 		final int tgx = GeoEngine.getInstance().getGeoX(tx); | ||||
| 		final int tgy = GeoEngine.getInstance().getGeoY(ty); | ||||
| 		 | ||||
| 		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); | ||||
| 		iter.next(); | ||||
| 		int prevX = iter.x(); | ||||
| 		int prevY = iter.y(); | ||||
| 		int wx = GeoEngine.getWorldX(prevX); | ||||
| 		int wy = GeoEngine.getWorldY(prevY); | ||||
| 		int wx = GeoEngine.getInstance().getWorldX(prevX); | ||||
| 		int wy = GeoEngine.getInstance().getWorldY(prevY); | ||||
| 		int wz = iter.z(); | ||||
| 		prim.addPoint(Color.RED, wx, wy, wz); | ||||
| 		 | ||||
| @@ -78,8 +78,8 @@ public final class GeoUtils | ||||
| 			 | ||||
| 			if ((curX != prevX) || (curY != prevY)) | ||||
| 			{ | ||||
| 				wx = GeoEngine.getWorldX(curX); | ||||
| 				wy = GeoEngine.getWorldY(curY); | ||||
| 				wx = GeoEngine.getInstance().getWorldX(curX); | ||||
| 				wy = GeoEngine.getInstance().getWorldY(curY); | ||||
| 				wz = iter.z(); | ||||
| 				 | ||||
| 				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) | ||||
| 	{ | ||||
| 		if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) | ||||
| 		if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) | ||||
| 		{ | ||||
| 			return Color.GREEN; | ||||
| 		} | ||||
| @@ -109,8 +109,9 @@ public final class GeoUtils | ||||
| 		int iPacket = 0; | ||||
| 		 | ||||
| 		ExServerPrimitive exsp = null; | ||||
| 		final int playerGx = GeoEngine.getGeoX(player.getX()); | ||||
| 		final int playerGy = GeoEngine.getGeoY(player.getY()); | ||||
| 		final GeoEngine ge = GeoEngine.getInstance(); | ||||
| 		final int playerGx = ge.getGeoX(player.getX()); | ||||
| 		final int playerGy = ge.getGeoY(player.getY()); | ||||
| 		for (int dx = -geoRadius; dx <= geoRadius; ++dx) | ||||
| 		{ | ||||
| 			for (int dy = -geoRadius; dy <= geoRadius; ++dy) | ||||
| @@ -134,32 +135,32 @@ public final class GeoUtils | ||||
| 				final int gx = playerGx + dx; | ||||
| 				final int gy = playerGy + dy; | ||||
| 				 | ||||
| 				final int x = GeoEngine.getWorldX(gx); | ||||
| 				final int y = GeoEngine.getWorldY(gy); | ||||
| 				final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); | ||||
| 				final int x = ge.getWorldX(gx); | ||||
| 				final int y = ge.getWorldY(gy); | ||||
| 				final int z = ge.getNearestZ(gx, gy, player.getZ()); | ||||
| 				 | ||||
| 				// 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 - 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 - 4, y - 4, z, x + 4, y - 4, z); | ||||
| 				 | ||||
| 				// 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 + 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 + 4, y - 4, z, x + 4, y + 4, z); | ||||
| 				 | ||||
| 				// 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 - 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 - 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 - 6, y - 2, z, x - 6, y + 2, 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) | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; | ||||
| 				return Cell.NSWE_SOUTH_EAST; | ||||
| 			} | ||||
| 			else if (y < lastY) | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; | ||||
| 				return Cell.NSWE_NORTH_EAST; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_E; | ||||
| 				return Cell.NSWE_EAST; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (x < lastX) // west | ||||
| 		{ | ||||
| 			if (y > lastY) | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; | ||||
| 				return Cell.NSWE_SOUTH_WEST; | ||||
| 			} | ||||
| 			else if (y < lastY) | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; | ||||
| 				return Cell.NSWE_NORTH_WEST; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_W; | ||||
| 				return Cell.NSWE_WEST; | ||||
| 			} | ||||
| 		} | ||||
| 		else // unchanged x | ||||
| 		{ | ||||
| 			if (y > lastY) | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_S; | ||||
| 				return Cell.NSWE_SOUTH; | ||||
| 			} | ||||
| 			else if (y < lastY) | ||||
| 			{ | ||||
| 				return GeoStructure.CELL_FLAG_N; | ||||
| 				return Cell.NSWE_NORTH; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 MobiusDevelopment
					MobiusDevelopment